(function(){
var hola_vjs_provider_require, hola_player_api;
var E = hola_player_api = {};
E.zdot = function(name){
    // zdot_stub:
    // zdot_stub:return {
    // zdot_stub:customer: {[=json it.customer]},
    // zdot_stub:json: {[=json it.json]},
    // zdot_stub:}[name];
    return {}[name];
};
E.customer = E.zdot('customer');
E.disable = function(){};
function hola_player_init(){
(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.hola_player = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_dereq_,module,exports){
(function (global){
(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.dashjs || (g.dashjs = {})).MediaPlayer = 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 _dereq_=="function"&&_dereq_;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 _dereq_=="function"&&_dereq_;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2015-2016, DASH Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  2. Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

(function (exports) {

    "use strict";

    /**
     *  Exceptions from regular ASCII. CodePoints are mapped to UTF-16 codes
     */

    var specialCea608CharsCodes = {
        0x2a: 0xe1, // lowercase a, acute accent
        0x5c: 0xe9, // lowercase e, acute accent
        0x5e: 0xed, // lowercase i, acute accent
        0x5f: 0xf3, // lowercase o, acute accent
        0x60: 0xfa, // lowercase u, acute accent
        0x7b: 0xe7, // lowercase c with cedilla
        0x7c: 0xf7, // division symbol
        0x7d: 0xd1, // uppercase N tilde
        0x7e: 0xf1, // lowercase n tilde
        0x7f: 0x2588, // Full block
        // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
        // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
        // THIS MEANS THAT \x50 MUST BE ADDED TO THE VALUES
        0x80: 0xae, // Registered symbol (R)
        0x81: 0xb0, // degree sign
        0x82: 0xbd, // 1/2 symbol
        0x83: 0xbf, // Inverted (open) question mark
        0x84: 0x2122, // Trademark symbol (TM)
        0x85: 0xa2, // Cents symbol
        0x86: 0xa3, // Pounds sterling
        0x87: 0x266a, // Music 8'th note
        0x88: 0xe0, // lowercase a, grave accent
        0x89: 0x20, // transparent space (regular)
        0x8a: 0xe8, // lowercase e, grave accent
        0x8b: 0xe2, // lowercase a, circumflex accent
        0x8c: 0xea, // lowercase e, circumflex accent
        0x8d: 0xee, // lowercase i, circumflex accent
        0x8e: 0xf4, // lowercase o, circumflex accent
        0x8f: 0xfb, // lowercase u, circumflex accent
        // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
        // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F
        0x90: 0xc1, // capital letter A with acute
        0x91: 0xc9, // capital letter E with acute
        0x92: 0xd3, // capital letter O with acute
        0x93: 0xda, // capital letter U with acute
        0x94: 0xdc, // capital letter U with diaresis
        0x95: 0xfc, // lowercase letter U with diaeresis
        0x96: 0x2018, // opening single quote
        0x97: 0xa1, // inverted exclamation mark
        0x98: 0x2a, // asterisk
        0x99: 0x2019, // closing single quote
        0x9a: 0x2501, // box drawings heavy horizontal
        0x9b: 0xa9, // copyright sign
        0x9c: 0x2120, // Service mark
        0x9d: 0x2022, // (round) bullet
        0x9e: 0x201c, // Left double quotation mark
        0x9f: 0x201d, // Right double quotation mark
        0xa0: 0xc0, // uppercase A, grave accent
        0xa1: 0xc2, // uppercase A, circumflex
        0xa2: 0xc7, // uppercase C with cedilla
        0xa3: 0xc8, // uppercase E, grave accent
        0xa4: 0xca, // uppercase E, circumflex
        0xa5: 0xcb, // capital letter E with diaresis
        0xa6: 0xeb, // lowercase letter e with diaresis
        0xa7: 0xce, // uppercase I, circumflex
        0xa8: 0xcf, // uppercase I, with diaresis
        0xa9: 0xef, // lowercase i, with diaresis
        0xaa: 0xd4, // uppercase O, circumflex
        0xab: 0xd9, // uppercase U, grave accent
        0xac: 0xf9, // lowercase u, grave accent
        0xad: 0xdb, // uppercase U, circumflex
        0xae: 0xab, // left-pointing double angle quotation mark
        0xaf: 0xbb, // right-pointing double angle quotation mark
        // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
        // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F
        0xb0: 0xc3, // Uppercase A, tilde
        0xb1: 0xe3, // Lowercase a, tilde
        0xb2: 0xcd, // Uppercase I, acute accent
        0xb3: 0xcc, // Uppercase I, grave accent
        0xb4: 0xec, // Lowercase i, grave accent
        0xb5: 0xd2, // Uppercase O, grave accent
        0xb6: 0xf2, // Lowercase o, grave accent
        0xb7: 0xd5, // Uppercase O, tilde
        0xb8: 0xf5, // Lowercase o, tilde
        0xb9: 0x7b, // Open curly brace
        0xba: 0x7d, // Closing curly brace
        0xbb: 0x5c, // Backslash
        0xbc: 0x5e, // Caret
        0xbd: 0x5f, // Underscore
        0xbe: 0x7c, // Pipe (vertical line)
        0xbf: 0x223c, // Tilde operator
        0xc0: 0xc4, // Uppercase A, umlaut
        0xc1: 0xe4, // Lowercase A, umlaut
        0xc2: 0xd6, // Uppercase O, umlaut
        0xc3: 0xf6, // Lowercase o, umlaut
        0xc4: 0xdf, // Esszett (sharp S)
        0xc5: 0xa5, // Yen symbol
        0xc6: 0xa4, // Generic currency sign
        0xc7: 0x2503, // Box drawings heavy vertical
        0xc8: 0xc5, // Uppercase A, ring
        0xc9: 0xe5, // Lowercase A, ring
        0xca: 0xd8, // Uppercase O, stroke
        0xcb: 0xf8, // Lowercase o, strok
        0xcc: 0x250f, // Box drawings heavy down and right
        0xcd: 0x2513, // Box drawings heavy down and left
        0xce: 0x2517, // Box drawings heavy up and right
        0xcf: 0x251b // Box drawings heavy up and left
    };

    /**
     * Get Unicode Character from CEA-608 byte code
     */
    var getCharForByte = function getCharForByte(byte) {
        var charCode = byte;
        if (specialCea608CharsCodes.hasOwnProperty(byte)) {
            charCode = specialCea608CharsCodes[byte];
        }
        return String.fromCharCode(charCode);
    };

    var NR_ROWS = 15,
        NR_COLS = 32;
    // Tables to look up row from PAC data
    var rowsLowCh1 = { 0x11: 1, 0x12: 3, 0x15: 5, 0x16: 7, 0x17: 9, 0x10: 11, 0x13: 12, 0x14: 14 };
    var rowsHighCh1 = { 0x11: 2, 0x12: 4, 0x15: 6, 0x16: 8, 0x17: 10, 0x13: 13, 0x14: 15 };
    var rowsLowCh2 = { 0x19: 1, 0x1A: 3, 0x1D: 5, 0x1E: 7, 0x1F: 9, 0x18: 11, 0x1B: 12, 0x1C: 14 };
    var rowsHighCh2 = { 0x19: 2, 0x1A: 4, 0x1D: 6, 0x1E: 8, 0x1F: 10, 0x1B: 13, 0x1C: 15 };

    var backgroundColors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'black', 'transparent'];

    /**
     * Simple logger class to be able to write with time-stamps and filter on level.
     */
    var logger = {
        verboseFilter: { 'DATA': 3, 'DEBUG': 3, 'INFO': 2, 'WARNING': 2, 'TEXT': 1, 'ERROR': 0 },
        time: null,
        verboseLevel: 0, // Only write errors
        setTime: function setTime(newTime) {
            this.time = newTime;
        },
        log: function log(severity, msg) {
            var minLevel = this.verboseFilter[severity];
            if (this.verboseLevel >= minLevel) {
                console.log(this.time + " [" + severity + "] " + msg);
            }
        }
    };

    var numArrayToHexArray = function numArrayToHexArray(numArray) {
        var hexArray = [];
        for (var j = 0; j < numArray.length; j++) {
            hexArray.push(numArray[j].toString(16));
        }
        return hexArray;
    };

    /**
     * State of CEA-608 pen or character
     * @constructor
     */
    var PenState = function PenState(foreground, underline, italics, background, flash) {
        this.foreground = foreground || "white";
        this.underline = underline || false;
        this.italics = italics || false;
        this.background = background || "black";
        this.flash = flash || false;
    };

    PenState.prototype = {

        reset: function reset() {
            this.foreground = "white";
            this.underline = false;
            this.italics = false;
            this.background = "black";
            this.flash = false;
        },

        setStyles: function setStyles(styles) {
            var attribs = ["foreground", "underline", "italics", "background", "flash"];
            for (var i = 0; i < attribs.length; i++) {
                var style = attribs[i];
                if (styles.hasOwnProperty(style)) {
                    this[style] = styles[style];
                }
            }
        },

        isDefault: function isDefault() {
            return this.foreground === "white" && !this.underline && !this.italics && this.background === "black" && !this.flash;
        },

        equals: function equals(other) {
            return this.foreground === other.foreground && this.underline === other.underline && this.italics === other.italics && this.background === other.background && this.flash === other.flash;
        },

        copy: function copy(newPenState) {
            this.foreground = newPenState.foreground;
            this.underline = newPenState.underline;
            this.italics = newPenState.italics;
            this.background = newPenState.background;
            this.flash = newPenState.flash;
        },

        toString: function toString() {
            return "color=" + this.foreground + ", underline=" + this.underline + ", italics=" + this.italics + ", background=" + this.background + ", flash=" + this.flash;
        }
    };

    /**
     * Unicode character with styling and background.
     * @constructor
     */
    var StyledUnicodeChar = function StyledUnicodeChar(uchar, foreground, underline, italics, background, flash) {
        this.uchar = uchar || ' '; // unicode character
        this.penState = new PenState(foreground, underline, italics, background, flash);
    };

    StyledUnicodeChar.prototype = {

        reset: function reset() {
            this.uchar = ' ';
            this.penState.reset();
        },

        setChar: function setChar(uchar, newPenState) {
            this.uchar = uchar;
            this.penState.copy(newPenState);
        },

        setPenState: function setPenState(newPenState) {
            this.penState.copy(newPenState);
        },

        equals: function equals(other) {
            return this.uchar === other.uchar && this.penState.equals(other.penState);
        },

        copy: function copy(newChar) {
            this.uchar = newChar.uchar;
            this.penState.copy(newChar.penState);
        },

        isEmpty: function isEmpty() {
            return this.uchar === ' ' && this.penState.isDefault();
        }
    };

    /**
     * CEA-608 row consisting of NR_COLS instances of StyledUnicodeChar.
     * @constructor
     */
    var Row = function Row() {
        this.chars = [];
        for (var i = 0; i < NR_COLS; i++) {
            this.chars.push(new StyledUnicodeChar());
        }
        this.pos = 0;
        this.currPenState = new PenState();
    };

    Row.prototype = {

        equals: function equals(other) {
            var equal = true;
            for (var i = 0; i < NR_COLS; i++) {
                if (!this.chars[i].equals(other.chars[i])) {
                    equal = false;
                    break;
                }
            }
            return equal;
        },

        copy: function copy(other) {
            for (var i = 0; i < NR_COLS; i++) {
                this.chars[i].copy(other.chars[i]);
            }
        },

        isEmpty: function isEmpty() {
            var empty = true;
            for (var i = 0; i < NR_COLS; i++) {
                if (!this.chars[i].isEmpty()) {
                    empty = false;
                    break;
                }
            }
            return empty;
        },

        /**
         *  Set the cursor to a valid column.
         */
        setCursor: function setCursor(absPos) {
            if (this.pos !== absPos) {
                this.pos = absPos;
            }
            if (this.pos < 0) {
                logger.log("ERROR", "Negative cursor position " + this.pos);
                this.pos = 0;
            } else if (this.pos > NR_COLS) {
                logger.log("ERROR", "Too large cursor position " + this.pos);
                this.pos = NR_COLS;
            }
        },

        /** 
         * Move the cursor relative to current position.
         */
        moveCursor: function moveCursor(relPos) {
            var newPos = this.pos + relPos;
            if (relPos > 1) {
                for (var i = this.pos + 1; i < newPos + 1; i++) {
                    this.chars[i].setPenState(this.currPenState);
                }
            }
            this.setCursor(newPos);
        },

        /**
         * Backspace, move one step back and clear character.
         */
        backSpace: function backSpace() {
            this.moveCursor(-1);
            this.chars[this.pos].setChar(' ', this.currPenState);
        },

        insertChar: function insertChar(byte) {
            if (byte >= 0x90) {
                //Extended char
                this.backSpace();
            }
            var char = getCharForByte(byte);
            if (this.pos >= NR_COLS) {
                logger.log("ERROR", "Cannot insert " + byte.toString(16) + " (" + char + ") at position " + this.pos + ". Skipping it!");
                return;
            }
            this.chars[this.pos].setChar(char, this.currPenState);
            this.moveCursor(1);
        },

        clearFromPos: function clearFromPos(startPos) {
            var i;
            for (i = startPos; i < NR_COLS; i++) {
                this.chars[i].reset();
            }
        },

        clear: function clear() {
            this.clearFromPos(0);
            this.pos = 0;
            this.currPenState.reset();
        },

        clearToEndOfRow: function clearToEndOfRow() {
            this.clearFromPos(this.pos);
        },

        getTextString: function getTextString() {
            var chars = [];
            var empty = true;
            for (var i = 0; i < NR_COLS; i++) {
                var char = this.chars[i].uchar;
                if (char !== " ") {
                    empty = false;
                }
                chars.push(char);
            }
            if (empty) {
                return "";
            } else {
                return chars.join("");
            }
        },

        setPenStyles: function setPenStyles(styles) {
            this.currPenState.setStyles(styles);
            var currChar = this.chars[this.pos];
            currChar.setPenState(this.currPenState);
        }
    };

    /**
     * Keep a CEA-608 screen of 32x15 styled characters
     * @constructor
    */
    var CaptionScreen = function CaptionScreen() {

        this.rows = [];
        for (var i = 0; i < NR_ROWS; i++) {
            this.rows.push(new Row()); // Note that we use zero-based numbering (0-14)
        }
        this.currRow = NR_ROWS - 1;
        this.nrRollUpRows = null;
        this.reset();
    };

    CaptionScreen.prototype = {

        reset: function reset() {
            for (var i = 0; i < NR_ROWS; i++) {
                this.rows[i].clear();
            }
            this.currRow = NR_ROWS - 1;
        },

        equals: function equals(other) {
            var equal = true;
            for (var i = 0; i < NR_ROWS; i++) {
                if (!this.rows[i].equals(other.rows[i])) {
                    equal = false;
                    break;
                }
            }
            return equal;
        },

        copy: function copy(other) {
            for (var i = 0; i < NR_ROWS; i++) {
                this.rows[i].copy(other.rows[i]);
            }
        },

        isEmpty: function isEmpty() {
            var empty = true;
            for (var i = 0; i < NR_ROWS; i++) {
                if (!this.rows[i].isEmpty()) {
                    empty = false;
                    break;
                }
            }
            return empty;
        },

        backSpace: function backSpace() {
            var row = this.rows[this.currRow];
            row.backSpace();
        },

        clearToEndOfRow: function clearToEndOfRow() {
            var row = this.rows[this.currRow];
            row.clearToEndOfRow();
        },

        /**
         * Insert a character (without styling) in the current row.
         */
        insertChar: function insertChar(char) {
            var row = this.rows[this.currRow];
            row.insertChar(char);
        },

        setPen: function setPen(styles) {
            var row = this.rows[this.currRow];
            row.setPenStyles(styles);
        },

        moveCursor: function moveCursor(relPos) {
            var row = this.rows[this.currRow];
            row.moveCursor(relPos);
        },

        setCursor: function setCursor(absPos) {
            logger.log("INFO", "setCursor: " + absPos);
            var row = this.rows[this.currRow];
            row.setCursor(absPos);
        },

        setPAC: function setPAC(pacData) {
            logger.log("INFO", "pacData = " + JSON.stringify(pacData));
            var newRow = pacData.row - 1;
            if (this.nrRollUpRows && newRow < this.nrRollUpRows - 1) {
                newRow = this.nrRollUpRows - 1;
            }
            this.currRow = newRow;
            var row = this.rows[this.currRow];
            if (pacData.indent !== null) {
                var indent = pacData.indent;
                var prevPos = Math.max(indent - 1, 0);
                row.setCursor(pacData.indent);
                pacData.color = row.chars[prevPos].penState.foreground;
            }
            var styles = { foreground: pacData.color, underline: pacData.underline, italics: pacData.italics, background: 'black', flash: false };
            this.setPen(styles);
        },

        /**
         * Set background/extra foreground, but first do back_space, and then insert space (backwards compatibility).
         */
        setBkgData: function setBkgData(bkgData) {

            logger.log("INFO", "bkgData = " + JSON.stringify(bkgData));
            this.backSpace();
            this.setPen(bkgData);
            this.insertChar(0x20); //Space
        },

        setRollUpRows: function setRollUpRows(nrRows) {
            this.nrRollUpRows = nrRows;
        },

        rollUp: function rollUp() {
            if (this.nrRollUpRows === null) {
                logger.log("DEBUG", "roll_up but nrRollUpRows not set yet");
                return; //Not properly setup
            }
            logger.log("TEXT", this.getDisplayText());
            var topRowIndex = this.currRow + 1 - this.nrRollUpRows;
            var topRow = this.rows.splice(topRowIndex, 1)[0];
            topRow.clear();
            this.rows.splice(this.currRow, 0, topRow);
            logger.log("INFO", "Rolling up");
            //logger.log("TEXT", this.get_display_text())
        },

        /**
         * Get all non-empty rows with as unicode text. 
         */
        getDisplayText: function getDisplayText(asOneRow) {
            asOneRow = asOneRow || false;
            var displayText = [];
            var text = "";
            var rowNr = -1;
            for (var i = 0; i < NR_ROWS; i++) {
                var rowText = this.rows[i].getTextString();
                if (rowText) {
                    rowNr = i + 1;
                    if (asOneRow) {
                        displayText.push("Row " + rowNr + ': "' + rowText + '"');
                    } else {
                        displayText.push(rowText.trim());
                    }
                }
            }
            if (displayText.length > 0) {
                if (asOneRow) {
                    text = "[" + displayText.join(" | ") + "]";
                } else {
                    text = displayText.join("\n");
                }
            }
            return text;
        },

        getTextAndFormat: function getTextAndFormat() {
            return this.rows;
        }
    };

    /**
     * Handle a CEA-608 channel and send decoded data to outputFilter
     * @constructor
     * @param {Number} channelNumber (1 or 2)
     * @param {CueHandler} outputFilter Output from channel1 newCue(startTime, endTime, captionScreen)
    */
    var Cea608Channel = function Cea608Channel(channelNumber, outputFilter) {

        this.chNr = channelNumber;
        this.outputFilter = outputFilter;
        this.mode = null;
        this.verbose = 0;
        this.displayedMemory = new CaptionScreen();
        this.nonDisplayedMemory = new CaptionScreen();
        this.lastOutputScreen = new CaptionScreen();
        this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1];
        this.writeScreen = this.displayedMemory;
        this.mode = null;
        this.cueStartTime = null; // Keeps track of where a cue started.
    };

    Cea608Channel.prototype = {

        modes: ["MODE_ROLL-UP", "MODE_POP-ON", "MODE_PAINT-ON", "MODE_TEXT"],

        reset: function reset() {
            this.mode = null;
            this.displayedMemory.reset();
            this.nonDisplayedMemory.reset();
            this.lastOutputScreen.reset();
            this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1];
            this.writeScreen = this.displayedMemory;
            this.mode = null;
            this.cueStartTime = null;
            this.lastCueEndTime = null;
        },

        getHandler: function getHandler() {
            return this.outputFilter;
        },

        setHandler: function setHandler(newHandler) {
            this.outputFilter = newHandler;
        },

        setPAC: function setPAC(pacData) {
            this.writeScreen.setPAC(pacData);
        },

        setBkgData: function setBkgData(bkgData) {
            this.writeScreen.setBkgData(bkgData);
        },

        setMode: function setMode(newMode) {
            if (newMode === this.mode) {
                return;
            }
            this.mode = newMode;
            logger.log("INFO", "MODE=" + newMode);
            if (this.mode == "MODE_POP-ON") {
                this.writeScreen = this.nonDisplayedMemory;
            } else {
                this.writeScreen = this.displayedMemory;
                this.writeScreen.reset();
            }
            if (this.mode !== "MODE_ROLL-UP") {
                this.displayedMemory.nrRollUpRows = null;
                this.nonDisplayedMemory.nrRollUpRows = null;
            }
            this.mode = newMode;
        },

        insertChars: function insertChars(chars) {
            for (var i = 0; i < chars.length; i++) {
                this.writeScreen.insertChar(chars[i]);
            }
            var screen = this.writeScreen === this.displayedMemory ? "DISP" : "NON_DISP";
            logger.log("INFO", screen + ": " + this.writeScreen.getDisplayText(true));
            if (this.mode === "MODE_PAINT-ON" || this.mode === "MODE_ROLL-UP") {
                logger.log("TEXT", "DISPLAYED: " + this.displayedMemory.getDisplayText(true));
                this.outputDataUpdate();
            }
        },

        cc_RCL: function cc_RCL() {
            // Resume Caption Loading (switch mode to Pop On)
            logger.log("INFO", "RCL - Resume Caption Loading");
            this.setMode("MODE_POP-ON");
        },
        cc_BS: function cc_BS() {
            // BackSpace
            logger.log("INFO", "BS - BackSpace");
            if (this.mode === "MODE_TEXT") {
                return;
            }
            this.writeScreen.backSpace();
            if (this.writeScreen === this.displayedMemory) {
                this.outputDataUpdate();
            }
        },
        cc_AOF: function cc_AOF() {
            // Reserved (formerly Alarm Off)
            return;
        },
        cc_AON: function cc_AON() {
            // Reserved (formerly Alarm On)
            return;
        },
        cc_DER: function cc_DER() {
            // Delete to End of Row
            logger.log("INFO", "DER- Delete to End of Row");
            this.writeScreen.clearToEndOfRow();
            this.outputDataUpdate();
        },
        cc_RU: function cc_RU(nrRows) {
            //Roll-Up Captions-2,3,or 4 Rows
            logger.log("INFO", "RU(" + nrRows + ") - Roll Up");
            this.writeScreen = this.displayedMemory;
            this.setMode("MODE_ROLL-UP");
            this.writeScreen.setRollUpRows(nrRows);
        },
        cc_FON: function cc_FON() {
            //Flash On
            logger.log("INFO", "FON - Flash On");
            this.writeScreen.setPen({ flash: true });
        },
        cc_RDC: function cc_RDC() {
            // Resume Direct Captioning (switch mode to PaintOn)
            logger.log("INFO", "RDC - Resume Direct Captioning");
            this.setMode("MODE_PAINT-ON");
        },
        cc_TR: function cc_TR() {
            // Text Restart in text mode (not supported, however)
            logger.log("INFO", "TR");
            this.setMode("MODE_TEXT");
        },
        cc_RTD: function cc_RTD() {
            // Resume Text Display in Text mode (not supported, however)
            logger.log("INFO", "RTD");
            this.setMode("MODE_TEXT");
        },
        cc_EDM: function cc_EDM() {
            // Erase Displayed Memory
            logger.log("INFO", "EDM - Erase Displayed Memory");
            this.displayedMemory.reset();
            this.outputDataUpdate();
        },
        cc_CR: function cc_CR() {
            // Carriage Return
            logger.log("CR - Carriage Return");
            this.writeScreen.rollUp();
            this.outputDataUpdate();
        },
        cc_ENM: function cc_ENM() {
            //Erase Non-Displayed Memory
            logger.log("INFO", "ENM - Erase Non-displayed Memory");
            this.nonDisplayedMemory.reset();
        },
        cc_EOC: function cc_EOC() {
            //End of Caption (Flip Memories)
            logger.log("INFO", "EOC - End Of Caption");
            if (this.mode === "MODE_POP-ON") {
                var tmp = this.displayedMemory;
                this.displayedMemory = this.nonDisplayedMemory;
                this.nonDisplayedMemory = tmp;
                this.writeScreen = this.nonDisplayedMemory;
                logger.log("TEXT", "DISP: " + this.displayedMemory.getDisplayText());
            }
            this.outputDataUpdate();
        },
        cc_TO: function cc_TO(nrCols) {
            // Tab Offset 1,2, or 3 columns
            logger.log("INFO", "TO(" + nrCols + ") - Tab Offset");
            this.writeScreen.moveCursor(nrCols);
        },
        cc_MIDROW: function cc_MIDROW(secondByte) {
            // Parse MIDROW command
            var styles = { flash: false };
            styles.underline = secondByte % 2 === 1;
            styles.italics = secondByte >= 0x2e;
            if (!styles.italics) {
                var colorIndex = Math.floor(secondByte / 2) - 0x10;
                var colors = ["white", "green", "blue", "cyan", "red", "yellow", "magenta"];
                styles.foreground = colors[colorIndex];
            } else {
                styles.foreground = "white";
            }
            logger.log("INFO", "MIDROW: " + JSON.stringify(styles));
            this.writeScreen.setPen(styles);
        },

        outputDataUpdate: function outputDataUpdate() {
            var t = logger.time;
            if (t === null) {
                return;
            }
            if (this.outputFilter) {
                if (this.outputFilter.updateData) {
                    this.outputFilter.updateData(t, this.displayedMemory);
                }
                if (this.cueStartTime === null && !this.displayedMemory.isEmpty()) {
                    // Start of a new cue
                    this.cueStartTime = t;
                } else {
                    if (!this.displayedMemory.equals(this.lastOutputScreen)) {
                        if (this.outputFilter.newCue) {
                            this.outputFilter.newCue(this.cueStartTime, t, this.lastOutputScreen);
                        }
                        this.cueStartTime = this.displayedMemory.isEmpty() ? null : t;
                    }
                }
                this.lastOutputScreen.copy(this.displayedMemory);
            }
        },

        cueSplitAtTime: function cueSplitAtTime(t) {
            if (this.outputFilter) {
                if (!this.displayedMemory.isEmpty()) {
                    if (this.outputFilter.newCue) {
                        this.outputFilter.newCue(this.cueStartTime, t, this.displayedMemory);
                    }
                    this.cueStartTime = t;
                }
            }
        }
    };

    /**
     * Parse CEA-608 data and send decoded data to out1 and out2.
     * @constructor
     * @param {Number} field  CEA-608 field (1 or 2)
     * @param {CueHandler} out1 Output from channel1 newCue(startTime, endTime, captionScreen)
     * @param {CueHandler} out2 Output from channel2 newCue(startTime, endTime, captionScreen)
     */
    var Cea608Parser = function Cea608Parser(field, out1, out2) {
        this.field = field || 1;
        this.outputs = [out1, out2];
        this.channels = [new Cea608Channel(1, out1), new Cea608Channel(2, out2)];
        this.currChNr = -1; // Will be 1 or 2
        this.lastCmdA = null; // First byte of last command
        this.lastCmdB = null; // Second byte of last command
        this.bufferedData = [];
        this.startTime = null;
        this.lastTime = null;
        this.dataCounters = { 'padding': 0, 'char': 0, 'cmd': 0, 'other': 0 };
    };

    Cea608Parser.prototype = {

        getHandler: function getHandler(index) {
            return this.channels[index].getHandler();
        },

        setHandler: function setHandler(index, newHandler) {
            this.channels[index].setHandler(newHandler);
        },

        /**
         * Add data for time t in forms of list of bytes (unsigned ints). The bytes are treated as pairs.
         */
        addData: function addData(t, byteList) {
            var cmdFound,
                a,
                b,
                charsFound = false;

            this.lastTime = t;
            logger.setTime(t);

            for (var i = 0; i < byteList.length; i += 2) {
                a = byteList[i] & 0x7f;
                b = byteList[i + 1] & 0x7f;
                if (a === 0 && b === 0) {
                    this.dataCounters.padding += 2;
                    continue;
                } else {
                    logger.log("DATA", "[" + numArrayToHexArray([byteList[i], byteList[i + 1]]) + "] -> (" + numArrayToHexArray([a, b]) + ")");
                }
                cmdFound = this.parseCmd(a, b);
                if (!cmdFound) {
                    cmdFound = this.parseMidrow(a, b);
                }
                if (!cmdFound) {
                    cmdFound = this.parsePAC(a, b);
                }
                if (!cmdFound) {
                    cmdFound = this.parseBackgroundAttributes(a, b);
                }
                if (!cmdFound) {
                    charsFound = this.parseChars(a, b);
                    if (charsFound) {
                        if (this.currChNr && this.currChNr >= 0) {
                            var channel = this.channels[this.currChNr - 1];
                            channel.insertChars(charsFound);
                        } else {
                            logger.log("WARNING", "No channel found yet. TEXT-MODE?");
                        }
                    }
                }
                if (cmdFound) {
                    this.dataCounters.cmd += 2;
                } else if (charsFound) {
                    this.dataCounters.char += 2;
                } else {
                    this.dataCounters.other += 2;
                    logger.log("WARNING", "Couldn't parse cleaned data " + numArrayToHexArray([a, b]) + " orig: " + numArrayToHexArray([byteList[i], byteList[i + 1]]));
                }
            }
        },

        /**
         * Parse Command.
         * @returns {Boolean} Tells if a command was found
         */
        parseCmd: function parseCmd(a, b) {
            var chNr = null;

            var cond1 = (a === 0x14 || a === 0x1C) && 0x20 <= b && b <= 0x2F;
            var cond2 = (a === 0x17 || a === 0x1F) && 0x21 <= b && b <= 0x23;
            if (!(cond1 || cond2)) {
                return false;
            }

            if (a === this.lastCmdA && b === this.lastCmdB) {
                this.lastCmdA = null;
                this.lastCmdB = null; // Repeated commands are dropped (once)
                logger.log("DEBUG", "Repeated command (" + numArrayToHexArray([a, b]) + ") is dropped");
                return true;
            }

            if (a === 0x14 || a === 0x17) {
                chNr = 1;
            } else {
                chNr = 2; // (a === 0x1C || a=== 0x1f)
            }

            var channel = this.channels[chNr - 1];

            if (a === 0x14 || a === 0x1C) {
                if (b === 0x20) {
                    channel.cc_RCL();
                } else if (b === 0x21) {
                    channel.cc_BS();
                } else if (b === 0x22) {
                    channel.cc_AOF();
                } else if (b === 0x23) {
                    channel.cc_AON();
                } else if (b === 0x24) {
                    channel.cc_DER();
                } else if (b === 0x25) {
                    channel.cc_RU(2);
                } else if (b === 0x26) {
                    channel.cc_RU(3);
                } else if (b === 0x27) {
                    channel.cc_RU(4);
                } else if (b === 0x28) {
                    channel.cc_FON();
                } else if (b === 0x29) {
                    channel.cc_RDC();
                } else if (b === 0x2A) {
                    channel.cc_TR();
                } else if (b === 0x2B) {
                    channel.cc_RTD();
                } else if (b === 0x2C) {
                    channel.cc_EDM();
                } else if (b === 0x2D) {
                    channel.cc_CR();
                } else if (b === 0x2E) {
                    channel.cc_ENM();
                } else if (b === 0x2F) {
                    channel.cc_EOC();
                }
            } else {
                //a == 0x17 || a == 0x1F
                channel.cc_TO(b - 0x20);
            }
            this.lastCmdA = a;
            this.lastCmdB = b;
            this.currChNr = chNr;
            return true;
        },

        /**
         * Parse midrow styling command
         * @returns {Boolean}
         */
        parseMidrow: function parseMidrow(a, b) {
            var chNr = null;

            if ((a === 0x11 || a === 0x19) && 0x20 <= b && b <= 0x2f) {
                if (a === 0x11) {
                    chNr = 1;
                } else {
                    chNr = 2;
                }
                if (chNr !== this.currChNr) {
                    logger.log("ERROR", "Mismatch channel in midrow parsing");
                    return false;
                }
                var channel = this.channels[chNr - 1];
                channel.cc_MIDROW(b);
                logger.log("DEBUG", "MIDROW (" + numArrayToHexArray([a, b]) + ")");
                return true;
            }
            return false;
        },
        /**
         * Parse Preable Access Codes (Table 53).
         * @returns {Boolean} Tells if PAC found
         */
        parsePAC: function parsePAC(a, b) {

            var chNr = null;
            var row = null;

            var case1 = (0x11 <= a && a <= 0x17 || 0x19 <= a && a <= 0x1F) && 0x40 <= b && b <= 0x7F;
            var case2 = (a === 0x10 || a === 0x18) && 0x40 <= b && b <= 0x5F;
            if (!(case1 || case2)) {
                return false;
            }

            if (a === this.lastCmdA && b === this.lastCmdB) {
                this.lastCmdA = null;
                this.lastCmdB = null;
                return true; // Repeated commands are dropped (once)
            }

            chNr = a <= 0x17 ? 1 : 2;

            if (0x40 <= b && b <= 0x5F) {
                row = chNr === 1 ? rowsLowCh1[a] : rowsLowCh2[a];
            } else {
                // 0x60 <= b <= 0x7F
                row = chNr === 1 ? rowsHighCh1[a] : rowsHighCh2[a];
            }
            var pacData = this.interpretPAC(row, b);
            var channel = this.channels[chNr - 1];
            channel.setPAC(pacData);
            this.lastCmdA = a;
            this.lastCmdB = b;
            this.currChNr = chNr;
            return true;
        },

        /**
         * Interpret the second byte of the pac, and return the information.
         * @returns {Object} pacData with style parameters.
         */
        interpretPAC: function interpretPAC(row, byte) {
            var pacIndex = byte;
            var pacData = { color: null, italics: false, indent: null, underline: false, row: row };

            if (byte > 0x5F) {
                pacIndex = byte - 0x60;
            } else {
                pacIndex = byte - 0x40;
            }
            pacData.underline = (pacIndex & 1) === 1;
            if (pacIndex <= 0xd) {
                pacData.color = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'white'][Math.floor(pacIndex / 2)];
            } else if (pacIndex <= 0xf) {
                pacData.italics = true;
                pacData.color = 'white';
            } else {
                pacData.indent = Math.floor((pacIndex - 0x10) / 2) * 4;
            }
            return pacData; // Note that row has zero offset. The spec uses 1.
        },

        /**
         * Parse characters.
         * @returns An array with 1 to 2 codes corresponding to chars, if found. null otherwise.
         */
        parseChars: function parseChars(a, b) {

            var channelNr = null,
                charCodes = null,
                charCode1 = null,
                charCode2 = null;

            if (a >= 0x19) {
                channelNr = 2;
                charCode1 = a - 8;
            } else {
                channelNr = 1;
                charCode1 = a;
            }
            if (0x11 <= charCode1 && charCode1 <= 0x13) {
                // Special character
                var oneCode = b;
                if (charCode1 === 0x11) {
                    oneCode = b + 0x50;
                } else if (charCode1 === 0x12) {
                    oneCode = b + 0x70;
                } else {
                    oneCode = b + 0x90;
                }
                logger.log("INFO", "Special char '" + getCharForByte(oneCode) + "' in channel " + channelNr);
                charCodes = [oneCode];
            } else if (0x20 <= a && a <= 0x7f) {
                charCodes = b === 0 ? [a] : [a, b];
            }
            if (charCodes) {
                var hexCodes = numArrayToHexArray(charCodes);
                logger.log("DEBUG", "Char codes =  " + hexCodes.join(","));
                this.lastCmdA = null;
                this.lastCmdB = null;
            }
            return charCodes;
        },

        /**
        * Parse extended background attributes as well as new foreground color black.
        * @returns{Boolean} Tells if background attributes are found
        */
        parseBackgroundAttributes: function parseBackgroundAttributes(a, b) {
            var bkgData, index, chNr, channel;

            var case1 = (a === 0x10 || a === 0x18) && 0x20 <= b && b <= 0x2f;
            var case2 = (a === 0x17 || a === 0x1f) && 0x2d <= b && b <= 0x2f;
            if (!(case1 || case2)) {
                return false;
            }
            bkgData = {};
            if (a === 0x10 || a === 0x18) {
                index = Math.floor((b - 0x20) / 2);
                bkgData.background = backgroundColors[index];
                if (b % 2 === 1) {
                    bkgData.background = bkgData.background + "_semi";
                }
            } else if (b === 0x2d) {
                bkgData.background = "transparent";
            } else {
                bkgData.foreground = "black";
                if (b === 0x2f) {
                    bkgData.underline = true;
                }
            }
            chNr = a < 0x18 ? 1 : 2;
            channel = this.channels[chNr - 1];
            channel.setBkgData(bkgData);
            this.lastCmdA = null;
            this.lastCmdB = null;
            return true;
        },

        /**
         * Reset state of parser and its channels.
         */
        reset: function reset() {
            for (var i = 0; i < this.channels.length; i++) {
                if (this.channels[i]) {
                    this.channels[i].reset();
                }
            }
            this.lastCmdA = null;
            this.lastCmdB = null;
        },

        /**
         * Trigger the generation of a cue, and the start of a new one if displayScreens are not empty.
         */
        cueSplitAtTime: function cueSplitAtTime(t) {
            for (var i = 0; i < this.channels.length; i++) {
                if (this.channels[i]) {
                    this.channels[i].cueSplitAtTime(t);
                }
            }
        }
    };

    /**
     * Find ranges corresponding to SEA CEA-608 NALUS in sizeprepended NALU array.
     * @param {raw} dataView of binary data
     * @param {startPos} start position in raw
     * @param {size} total size of data in raw to consider
     * @returns 
     */
    var findCea608Nalus = function findCea608Nalus(raw, startPos, size) {
        var nalSize = 0,
            cursor = startPos,
            nalType = 0,
            cea608NaluRanges = [],

        // Check SEI data according to ANSI-SCTE 128
        isCEA608SEI = function isCEA608SEI(payloadType, payloadSize, raw, pos) {
            if (payloadType !== 4 || payloadSize < 8) {
                return null;
            }
            var countryCode = raw.getUint8(pos);
            var providerCode = raw.getUint16(pos + 1);
            var userIdentifier = raw.getUint32(pos + 3);
            var userDataTypeCode = raw.getUint8(pos + 7);
            return countryCode == 0xB5 && providerCode == 0x31 && userIdentifier == 0x47413934 && userDataTypeCode == 0x3;
        };
        while (cursor < startPos + size) {
            nalSize = raw.getUint32(cursor);
            nalType = raw.getUint8(cursor + 4) & 0x1F;
            //console.log(time + "  NAL " + nalType);
            if (nalType === 6) {
                // SEI NAL Unit. The NAL header is the first byte
                //console.log("SEI NALU of size " + nalSize + " at time " + time);
                var pos = cursor + 5;
                var payloadType = -1;
                while (pos < cursor + 4 + nalSize - 1) {
                    // The last byte should be rbsp_trailing_bits
                    payloadType = 0;
                    var b = 0xFF;
                    while (b === 0xFF) {
                        b = raw.getUint8(pos);
                        payloadType += b;
                        pos++;
                    }
                    var payloadSize = 0;
                    b = 0xFF;
                    while (b === 0xFF) {
                        b = raw.getUint8(pos);
                        payloadSize += b;
                        pos++;
                    }
                    if (isCEA608SEI(payloadType, payloadSize, raw, pos)) {
                        //console.log("CEA608 SEI " + time + " " + payloadSize);
                        cea608NaluRanges.push([pos, payloadSize]);
                    }
                    pos += payloadSize;
                }
            }
            cursor += nalSize + 4;
        }
        return cea608NaluRanges;
    };

    var extractCea608DataFromRange = function extractCea608DataFromRange(raw, cea608Range) {
        var pos = cea608Range[0];
        var fieldData = [[], []];

        pos += 8; // Skip the identifier up to userDataTypeCode
        var ccCount = raw.getUint8(pos) & 0x1f;
        pos += 2; // Advance 1 and skip reserved byte

        for (var i = 0; i < ccCount; i++) {
            var byte = raw.getUint8(pos);
            var ccValid = byte & 0x4;
            var ccType = byte & 0x3;
            pos++;
            var ccData1 = raw.getUint8(pos); // Keep parity bit
            pos++;
            var ccData2 = raw.getUint8(pos); // Keep parity bit
            pos++;
            if (ccValid && (ccData1 & 0x7f) + (ccData2 & 0x7f) !== 0) {
                //Check validity and non-empty data
                if (ccType === 0) {
                    fieldData[0].push(ccData1);
                    fieldData[0].push(ccData2);
                } else if (ccType === 1) {
                    fieldData[1].push(ccData1);
                    fieldData[1].push(ccData2);
                }
            }
        }
        return fieldData;
    };

    exports.logger = logger;
    exports.PenState = PenState;
    exports.CaptionScreen = CaptionScreen;
    exports.Cea608Parser = Cea608Parser;
    exports.findCea608Nalus = findCea608Nalus;
    exports.extractCea608DataFromRange = extractCea608DataFromRange;
})(typeof exports === 'undefined' ? undefined.cea608parser = {} : exports);

},{}],2:[function(_dereq_,module,exports){
/*
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * author Digital Primates
 * copyright dash-if 2012
 */

/*
 * var parent,
 *     child,
 *     properties = [
                    {
                        name: 'profiles',
                        merge: false
                    }
                ];
 *
 * parent = {};
 * parent.name = "ParentNode";
 * parent.isRoor = false;
 * parent.isArray = false;
 * parent.parent = null;
 * parent.children = [];
 * parent.properties = properties;
 *
 * child = {};
 * child.name = "ChildNode";
 * child.isRoor = false;
 * child.isArray = true;
 * child.parent = parent;
 * child.children = null;
 * child.properties = properties;
 * parent.children.push(child);
 *
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});
function ObjectIron(map) {

    var lookup, len, i;

    // create a list of top level items to search for
    lookup = [];
    for (i = 0, len = map.length; i < len; i += 1) {
        if (map[i].isRoot) {
            lookup.push("root");
        } else {
            lookup.push(map[i].name);
        }
    }

    var mergeValues = function mergeValues(parentItem, childItem) {
        var name, parentValue, childValue;

        if (parentItem === null || childItem === null) {
            return;
        }

        for (name in parentItem) {
            if (parentItem.hasOwnProperty(name)) {
                if (!childItem.hasOwnProperty(name)) {
                    childItem[name] = parentItem[name];
                }
            }
        }
    },
        mapProperties = function mapProperties(properties, parent, child) {
        var i, len, property, parentValue, childValue;

        if (properties === null || properties.length === 0) {
            return;
        }

        for (i = 0, len = properties.length; i < len; i += 1) {
            property = properties[i];

            if (parent.hasOwnProperty(property.name)) {
                if (child.hasOwnProperty(property.name)) {
                    // check to see if we should merge
                    if (property.merge) {
                        parentValue = parent[property.name];
                        childValue = child[property.name];

                        // complex objects; merge properties
                        if (typeof parentValue === 'object' && typeof childValue === 'object') {
                            mergeValues(parentValue, childValue);
                        }
                        // simple objects; merge them together
                        else {
                                if (property.mergeFunction != null) {
                                    child[property.name] = property.mergeFunction(parentValue, childValue);
                                } else {
                                    child[property.name] = parentValue + childValue;
                                }
                            }
                    }
                } else {
                    // just add the property
                    child[property.name] = parent[property.name];
                }
            }
        }
    },
        mapItem = function mapItem(obj, node) {
        var item = obj,
            i,
            len,
            v,
            len2,
            array,
            childItem,
            childNode,
            property;

        if (item.children === null || item.children.length === 0) {
            return;
        }

        for (i = 0, len = item.children.length; i < len; i += 1) {
            childItem = item.children[i];

            if (node.hasOwnProperty(childItem.name)) {
                if (childItem.isArray) {
                    array = node[childItem.name + "_asArray"];
                    for (v = 0, len2 = array.length; v < len2; v += 1) {
                        childNode = array[v];
                        mapProperties(item.properties, node, childNode);
                        mapItem(childItem, childNode);
                    }
                } else {
                    childNode = node[childItem.name];
                    mapProperties(item.properties, node, childNode);
                    mapItem(childItem, childNode);
                }
            }
        }
    },
        performMapping = function performMapping(source) {
        var i, len, pi, pp, item, node, array;

        if (source === null) {
            return source;
        }

        if (typeof source !== 'object') {
            return source;
        }

        // first look to see if anything cares about the root node
        for (i = 0, len = lookup.length; i < len; i += 1) {
            if (lookup[i] === "root") {
                item = map[i];
                node = source;
                mapItem(item, node);
            }
        }

        // iterate over the objects and look for any of the items we care about
        for (pp in source) {
            if (source.hasOwnProperty(pp) && pp != "__children") {
                pi = lookup.indexOf(pp);
                if (pi !== -1) {
                    item = map[pi];

                    if (item.isArray) {
                        array = source[pp + "_asArray"];
                        for (i = 0, len = array.length; i < len; i += 1) {
                            node = array[i];
                            mapItem(item, node);
                        }
                    } else {
                        node = source[pp];
                        mapItem(item, node);
                    }
                }
                // now check this to see if he has any of the properties we care about
                performMapping(source[pp]);
            }
        }

        return source;
    };

    return {
        run: performMapping
    };
}

exports['default'] = ObjectIron;
module.exports = exports['default'];

},{}],3:[function(_dereq_,module,exports){
/*
 Copyright 2011 Abdulla Abdurakhmanov
 Original sources are available at https://code.google.com/p/x2js/

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

/*
 Modified to keep track of children nodes in order in attribute __children.
*/

"use strict";

Object.defineProperty(exports, "__esModule", {
	value: true
});
function X2JS(matchers, attrPrefix, ignoreRoot) {
	if (attrPrefix === null || attrPrefix === undefined) {
		attrPrefix = "_";
	}

	if (ignoreRoot === null || ignoreRoot === undefined) {
		ignoreRoot = false;
	}

	var VERSION = "1.0.11";
	var escapeMode = false;

	var DOMNodeTypes = {
		ELEMENT_NODE: 1,
		TEXT_NODE: 3,
		CDATA_SECTION_NODE: 4,
		COMMENT_NODE: 8,
		DOCUMENT_NODE: 9
	};

	function getNodeLocalName(node) {
		var nodeLocalName = node.localName;
		if (nodeLocalName == null) // Yeah, this is IE!!
			nodeLocalName = node.baseName;
		if (nodeLocalName == null || nodeLocalName == "") // =="" is IE too
			nodeLocalName = node.nodeName;
		return nodeLocalName;
	}

	function getNodePrefix(node) {
		return node.prefix;
	}

	function escapeXmlChars(str) {
		if (typeof str == "string") return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g, '&#x2F;');else return str;
	}

	function unescapeXmlChars(str) {
		return str.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&#x27;/g, "'").replace(/&#x2F;/g, '\/');
	}

	function parseDOMChildren(node) {
		if (node.nodeType == DOMNodeTypes.DOCUMENT_NODE) {
			var result,
			    child = node.firstChild,
			    i,
			    len;

			// get the first node that isn't a comment
			for (i = 0, len = node.childNodes.length; i < len; i += 1) {
				if (node.childNodes[i].nodeType !== DOMNodeTypes.COMMENT_NODE) {
					child = node.childNodes[i];
					break;
				}
			}

			if (ignoreRoot) {
				result = parseDOMChildren(child);
			} else {
				result = {};
				var childName = getNodeLocalName(child);
				result[childName] = parseDOMChildren(child);
			}

			return result;
		} else if (node.nodeType == DOMNodeTypes.ELEMENT_NODE) {
			var result = new Object();
			result.__cnt = 0;

			var children = [];

			var nodeChildren = node.childNodes;

			// Children nodes
			for (var cidx = 0; cidx < nodeChildren.length; cidx++) {
				var child = nodeChildren.item(cidx); // nodeChildren[cidx];
				var childName = getNodeLocalName(child);

				result.__cnt++;
				if (result[childName] == null) {
					var c = parseDOMChildren(child);
					if (childName != "#text" || /[^\s]/.test(c)) {
						var o = {};
						o[childName] = c;
						children.push(o);
					}
					result[childName] = c;
					result[childName + "_asArray"] = new Array(1);
					result[childName + "_asArray"][0] = result[childName];
				} else {
					if (result[childName] != null) {
						if (!(result[childName] instanceof Array)) {
							var tmpObj = result[childName];
							result[childName] = new Array();
							result[childName][0] = tmpObj;

							result[childName + "_asArray"] = result[childName];
						}
					}
					var aridx = 0;
					while (result[childName][aridx] != null) aridx++;

					var c = parseDOMChildren(child);
					if (childName != "#text" || /[^\s]/.test(c)) {
						// Don't add white-space text nodes
						var o = {};
						o[childName] = c;
						children.push(o);
					}
					result[childName][aridx] = c;
				}
			}

			result.__children = children;

			// Attributes
			for (var aidx = 0; aidx < node.attributes.length; aidx++) {
				var attr = node.attributes.item(aidx); // [aidx];
				result.__cnt++;

				var value2 = attr.value;
				for (var m = 0, ml = matchers.length; m < ml; m++) {
					var matchobj = matchers[m];
					if (matchobj.test.call(this, attr)) value2 = matchobj.converter.call(this, attr.value);
				}

				result[attrPrefix + attr.name] = value2;
			}

			// Node namespace prefix
			var nodePrefix = getNodePrefix(node);
			if (nodePrefix != null && nodePrefix != "") {
				result.__cnt++;
				result.__prefix = nodePrefix;
			}

			if (result.__cnt == 1 && result["#text"] != null) {
				result = result["#text"];
			}

			if (result["#text"] != null) {
				result.__text = result["#text"];
				if (escapeMode) result.__text = unescapeXmlChars(result.__text);
				delete result["#text"];
				delete result["#text_asArray"];
			}
			if (result["#cdata-section"] != null) {
				result.__cdata = result["#cdata-section"];
				delete result["#cdata-section"];
				delete result["#cdata-section_asArray"];
			}

			if (result.__text != null || result.__cdata != null) {
				result.toString = function () {
					return (this.__text != null ? this.__text : '') + (this.__cdata != null ? this.__cdata : '');
				};
			}
			return result;
		} else if (node.nodeType == DOMNodeTypes.TEXT_NODE || node.nodeType == DOMNodeTypes.CDATA_SECTION_NODE) {
			return node.nodeValue;
		} else if (node.nodeType == DOMNodeTypes.COMMENT_NODE) {
			return null;
		}
	}

	function startTag(jsonObj, element, attrList, closed) {
		var resultStr = "<" + (jsonObj != null && jsonObj.__prefix != null ? jsonObj.__prefix + ":" : "") + element;
		if (attrList != null) {
			for (var aidx = 0; aidx < attrList.length; aidx++) {
				var attrName = attrList[aidx];
				var attrVal = jsonObj[attrName];
				resultStr += " " + attrName.substr(1) + "='" + attrVal + "'";
			}
		}
		if (!closed) resultStr += ">";else resultStr += "/>";
		return resultStr;
	}

	function endTag(jsonObj, elementName) {
		return "</" + (jsonObj.__prefix != null ? jsonObj.__prefix + ":" : "") + elementName + ">";
	}

	function endsWith(str, suffix) {
		return str.indexOf(suffix, str.length - suffix.length) !== -1;
	}

	function jsonXmlSpecialElem(jsonObj, jsonObjField) {
		if (endsWith(jsonObjField.toString(), "_asArray") || jsonObjField.toString().indexOf("_") == 0 || jsonObj[jsonObjField] instanceof Function) return true;else return false;
	}

	function jsonXmlElemCount(jsonObj) {
		var elementsCnt = 0;
		if (jsonObj instanceof Object) {
			for (var it in jsonObj) {
				if (jsonXmlSpecialElem(jsonObj, it)) continue;
				elementsCnt++;
			}
		}
		return elementsCnt;
	}

	function parseJSONAttributes(jsonObj) {
		var attrList = [];
		if (jsonObj instanceof Object) {
			for (var ait in jsonObj) {
				if (ait.toString().indexOf("__") == -1 && ait.toString().indexOf("_") == 0) {
					attrList.push(ait);
				}
			}
		}
		return attrList;
	}

	function parseJSONTextAttrs(jsonTxtObj) {
		var result = "";

		if (jsonTxtObj.__cdata != null) {
			result += "<![CDATA[" + jsonTxtObj.__cdata + "]]>";
		}

		if (jsonTxtObj.__text != null) {
			if (escapeMode) result += escapeXmlChars(jsonTxtObj.__text);else result += jsonTxtObj.__text;
		}
		return result;
	}

	function parseJSONTextObject(jsonTxtObj) {
		var result = "";

		if (jsonTxtObj instanceof Object) {
			result += parseJSONTextAttrs(jsonTxtObj);
		} else if (jsonTxtObj != null) {
			if (escapeMode) result += escapeXmlChars(jsonTxtObj);else result += jsonTxtObj;
		}

		return result;
	}

	function parseJSONArray(jsonArrRoot, jsonArrObj, attrList) {
		var result = "";
		if (jsonArrRoot.length == 0) {
			result += startTag(jsonArrRoot, jsonArrObj, attrList, true);
		} else {
			for (var arIdx = 0; arIdx < jsonArrRoot.length; arIdx++) {
				result += startTag(jsonArrRoot[arIdx], jsonArrObj, parseJSONAttributes(jsonArrRoot[arIdx]), false);
				result += parseJSONObject(jsonArrRoot[arIdx]);
				result += endTag(jsonArrRoot[arIdx], jsonArrObj);
			}
		}
		return result;
	}

	function parseJSONObject(jsonObj) {
		var result = "";

		var elementsCnt = jsonXmlElemCount(jsonObj);

		if (elementsCnt > 0) {
			for (var it in jsonObj) {

				if (jsonXmlSpecialElem(jsonObj, it)) continue;

				var subObj = jsonObj[it];

				var attrList = parseJSONAttributes(subObj);

				if (subObj == null || subObj == undefined) {
					result += startTag(subObj, it, attrList, true);
				} else if (subObj instanceof Object) {

					if (subObj instanceof Array) {
						result += parseJSONArray(subObj, it, attrList);
					} else {
						var subObjElementsCnt = jsonXmlElemCount(subObj);
						if (subObjElementsCnt > 0 || subObj.__text != null || subObj.__cdata != null) {
							result += startTag(subObj, it, attrList, false);
							result += parseJSONObject(subObj);
							result += endTag(subObj, it);
						} else {
							result += startTag(subObj, it, attrList, true);
						}
					}
				} else {
					result += startTag(subObj, it, attrList, false);
					result += parseJSONTextObject(subObj);
					result += endTag(subObj, it);
				}
			}
		}
		result += parseJSONTextObject(jsonObj);

		return result;
	}

	this.parseXmlString = function (xmlDocStr) {
		var xmlDoc, parser, ns;

		if (window.DOMParser) {
			parser = new window.DOMParser();

			try {
				ns = parser.parseFromString('<', 'text/xml').getElementsByTagName("parsererror")[0].namespaceURI;
			} catch (e) {
				// IE11 will definitely throw SyntaxError here
				// ns will be undefined
			}

			try {
				xmlDoc = parser.parseFromString(xmlDocStr, "text/xml");

				if (ns) {
					if (xmlDoc.getElementsByTagNameNS(ns, 'parsererror').length) {
						xmlDoc = undefined;
					}
				}
			} catch (e) {
				// IE11 may throw SyntaxError here if xmlDocStr is
				// not well formed. xmlDoc will be undefined
			}
		} else {
				// IE :(
				if (xmlDocStr.indexOf("<?") == 0) {
					xmlDocStr = xmlDocStr.substr(xmlDocStr.indexOf("?>") + 2);
				}
				xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
				xmlDoc.async = "false";
				xmlDoc.loadXML(xmlDocStr);
			}
		return xmlDoc;
	};

	this.xml2json = function (xmlDoc) {
		return parseDOMChildren(xmlDoc);
	};

	this.xml_str2json = function (xmlDocStr) {
		var xmlDoc = this.parseXmlString(xmlDocStr);
		return xmlDoc ? this.xml2json(xmlDoc) : undefined;
	};

	this.json2xml_str = function (jsonObj) {
		return parseJSONObject(jsonObj);
	};

	this.json2xml = function (jsonObj) {
		var xmlDocStr = this.json2xml_str(jsonObj);
		return this.parseXmlString(xmlDocStr);
	};

	this.getVersion = function () {
		return VERSION;
	};

	this.escapeMode = function (enabled) {
		escapeMode = enabled;
	};
}
exports["default"] = X2JS;
module.exports = exports["default"];

},{}],4:[function(_dereq_,module,exports){
/*! codem-isoboxer v0.2.8 https://github.com/madebyhiro/codem-isoboxer/blob/master/LICENSE.txt */
var ISOBoxer = {};

ISOBoxer.parseBuffer = function(arrayBuffer) {
  return new ISOFile(arrayBuffer).parse();
};

ISOBoxer.addBoxParser = function(type, parser) {
  if (typeof type !== 'string' || typeof parser !== 'function') {
    return;
  }
  ISOBox.prototype._boxParsers[type] = parser;
};

ISOBoxer.Utils = {};
ISOBoxer.Utils.dataViewToString = function(dataView, encoding) {
  var impliedEncoding = encoding || 'utf-8';
  if (typeof TextDecoder !== 'undefined') {
    return new TextDecoder(impliedEncoding).decode(dataView);
  }
  var a = [];
  var i = 0;

  if (impliedEncoding === 'utf-8') {
    /* The following algorithm is essentially a rewrite of the UTF8.decode at
    http://bannister.us/weblog/2007/simple-base64-encodedecode-javascript/
    */

    while (i < dataView.byteLength) {
      var c = dataView.getUint8(i++);
      if (c < 0x80) {
        // 1-byte character (7 bits)
      } else if (c < 0xe0) {
        // 2-byte character (11 bits)
        c = (c & 0x1f) << 6;
        c |= (dataView.getUint8(i++) & 0x3f);
      } else if (c < 0xf0) {
        // 3-byte character (16 bits)
        c = (c & 0xf) << 12;
        c |= (dataView.getUint8(i++) & 0x3f) << 6;
        c |= (dataView.getUint8(i++) & 0x3f);
      } else {
        // 4-byte character (21 bits)
        c = (c & 0x7) << 18;
        c |= (dataView.getUint8(i++) & 0x3f) << 12;
        c |= (dataView.getUint8(i++) & 0x3f) << 6;
        c |= (dataView.getUint8(i++) & 0x3f);
      }
      a.push(String.fromCharCode(c));
    }
  } else { // Just map byte-by-byte (probably wrong)
    while (i < dataView.byteLength) {
      a.push(String.fromCharCode(dataView.getUint8(i++)));
    }
  }
  return a.join('');
};

if (typeof exports !== 'undefined') {
  exports.parseBuffer  = ISOBoxer.parseBuffer;
  exports.addBoxParser = ISOBoxer.addBoxParser;
  exports.Utils        = ISOBoxer.Utils;
};
ISOBoxer.Cursor = function(initialOffset) {
  this.offset = (typeof initialOffset == 'undefined' ? 0 : initialOffset);
};;
var ISOFile = function(arrayBuffer) {
  this._raw = new DataView(arrayBuffer);
  this._cursor = new ISOBoxer.Cursor();
  this.boxes = [];
};

ISOFile.prototype.fetch = function(type) {
  var result = this.fetchAll(type, true);
  return (result.length ? result[0] : null);
};

ISOFile.prototype.fetchAll = function(type, returnEarly) {
  var result = [];
  ISOFile._sweep.call(this, type, result, returnEarly);
  return result;
};

ISOFile.prototype.parse = function() {
  this._cursor.offset = 0;
  this.boxes = [];
  while (this._cursor.offset < this._raw.byteLength) {
    var box = ISOBox.parse(this);

    // Box could not be parsed
    if (typeof box.type === 'undefined') break;

    this.boxes.push(box);
  }
  return this;
};

ISOFile._sweep = function(type, result, returnEarly) {
  if (this.type && this.type == type) result.push(this);
  for (var box in this.boxes) {
    if (result.length && returnEarly) return;
    ISOFile._sweep.call(this.boxes[box], type, result, returnEarly);
  }
};;
var ISOBox = function() {
  this._cursor = new ISOBoxer.Cursor();
};

ISOBox.parse = function(parent) {
  var newBox = new ISOBox();
  newBox._offset = parent._cursor.offset;
  newBox._root = (parent._root ? parent._root : parent);
  newBox._raw = parent._raw;
  newBox._parent = parent;
  newBox._parseBox();
  parent._cursor.offset = newBox._raw.byteOffset + newBox._raw.byteLength;
  return newBox;
};

ISOBox.prototype._readInt = function(size) {
  var result = null;
  switch(size) {
  case 8:
    result = this._raw.getInt8(this._cursor.offset - this._raw.byteOffset);
    break;
  case 16:
    result = this._raw.getInt16(this._cursor.offset - this._raw.byteOffset);
    break;
  case 32:
    result = this._raw.getInt32(this._cursor.offset - this._raw.byteOffset);
    break;
  case 64:
    // Warning: JavaScript cannot handle 64-bit integers natively.
    // This will give unexpected results for integers >= 2^53
    var s1 = this._raw.getInt32(this._cursor.offset - this._raw.byteOffset);
    var s2 = this._raw.getInt32(this._cursor.offset - this._raw.byteOffset + 4);
    result = (s1 * Math.pow(2,32)) + s2;
    break;
  }
  this._cursor.offset += (size >> 3);
  return result;
};

ISOBox.prototype._readUint = function(size) {
  var result = null,
      s1, s2;
  switch(size) {
  case 8:
    result = this._raw.getUint8(this._cursor.offset - this._raw.byteOffset);
    break;
  case 16:
    result = this._raw.getUint16(this._cursor.offset - this._raw.byteOffset);
    break;
  case 24:
    s1 = this._raw.getUint16(this._cursor.offset - this._raw.byteOffset);
    s2 = this._raw.getUint8(this._cursor.offset - this._raw.byteOffset + 2);
    result = (s1 << 8) + s2;
    break;
  case 32:
    result = this._raw.getUint32(this._cursor.offset - this._raw.byteOffset);
    break;
  case 64:
    // Warning: JavaScript cannot handle 64-bit integers natively.
    // This will give unexpected results for integers >= 2^53
    s1 = this._raw.getUint32(this._cursor.offset - this._raw.byteOffset);
    s2 = this._raw.getUint32(this._cursor.offset - this._raw.byteOffset + 4);
    result = (s1 * Math.pow(2,32)) + s2;
    break;
  }
  this._cursor.offset += (size >> 3);
  return result;
};

ISOBox.prototype._readString = function(length) {
  var str = '';
  for (var c = 0; c < length; c++) {
    var char = this._readUint(8);
    str += String.fromCharCode(char);
  }
  return str;
};

ISOBox.prototype._readTerminatedString = function() {
  var str = '';
  while (this._cursor.offset - this._offset < this._raw.byteLength) {
    var char = this._readUint(8);
    if (char === 0) break;
    str += String.fromCharCode(char);
  }
  return str;
};

ISOBox.prototype._readTemplate = function(size) {
  var pre = this._readUint(size / 2);
  var post = this._readUint(size / 2);
  return pre + (post / Math.pow(2, size / 2));
};

ISOBox.prototype._readData = function(size) {
  var length = (size > 0) ? size : (this._raw.byteLength - (this._cursor.offset - this._offset));
  var data = new DataView(this._raw.buffer, this._cursor.offset, length);
  this._cursor.offset += length;
  return data;
};

ISOBox.prototype._parseBox = function() {
  this._cursor.offset = this._offset;

  // return immediately if there are not enough bytes to read the header
  if (this._offset + 8 > this._raw.buffer.byteLength) {
    this._root._incomplete = true;
    return;
  }

  this.size = this._readUint(32);
  this.type = this._readString(4);

  if (this.size == 1)      { this.largesize = this._readUint(64); }
  if (this.type == 'uuid') { this.usertype = this._readString(16); }

  switch(this.size) {
  case 0:
    this._raw = new DataView(this._raw.buffer, this._offset, (this._raw.byteLength - this._cursor.offset));
    break;
  case 1:
    if (this._offset + this.size > this._raw.buffer.byteLength) {
      this._incomplete = true;
      this._root._incomplete = true;
    } else {
      this._raw = new DataView(this._raw.buffer, this._offset, this.largesize);
    }
    break;
  default:
    if (this._offset + this.size > this._raw.buffer.byteLength) {
      this._incomplete = true;
      this._root._incomplete = true;
    } else {
      this._raw = new DataView(this._raw.buffer, this._offset, this.size);
    }
  }

  // additional parsing
  if (!this._incomplete && this._boxParsers[this.type]) this._boxParsers[this.type].call(this);
};

ISOBox.prototype._parseFullBox = function() {
  this.version = this._readUint(8);
  this.flags = this._readUint(24);
};

ISOBox.prototype._boxParsers = {};;
// ISO/IEC 14496-15:2014 - avc1 box
ISOBox.prototype._boxParsers['avc1'] = function() {
  // SampleEntry fields
  this.reserved1 = [
    this._readUint(8),
    this._readUint(8),
    this._readUint(8),
    this._readUint(8),
    this._readUint(8),
    this._readUint(8)
  ];
  this.data_reference_index = this._readUint(16);
  // VisualSampleEntry fields
  this.pre_defined1 = this._readUint(16);
  this.reserved2 = this._readUint(16);
  this.pre_defined2 = [
    this._readUint(32),
    this._readUint(32),
    this._readUint(32)
  ];
  this.width                 = this._readUint(16);
  this.height                = this._readUint(16);
  this.horizresolution       = this._readTemplate(32);
  this.vertresolution        = this._readTemplate(32);
  this.reserved3             = this._readUint(32);
  this.frame_count           = this._readUint(16);
  var length = this._readUint(8);
  this.compressorname        = this._readString(length);
  for (var i = 0; i < (31 - length); i++) {
    this._readUint(8);
  }
  this.depth                 = this._readUint(16);
  this.pre_defined3          = this._readInt(16);
  // AVCSampleEntry fields
  this.config = this._readData();
};;
// Simple container boxes, all from ISO/IEC 14496-12:2012 except vttc which is from 14496-30.
[
  'moov', 'trak', 'tref', 'mdia', 'minf', 'stbl', 'edts', 'dinf',
  'mvex', 'moof', 'traf', 'mfra', 'udta', 'meco', 'strk', 'vttc'
].forEach(function(boxType) {
  ISOBox.prototype._boxParsers[boxType] = function() {
    this.boxes = [];
    while (this._cursor.offset - this._raw.byteOffset < this._raw.byteLength) {
      this.boxes.push(ISOBox.parse(this));
    }
  };
});
;
// ISO/IEC 14496-12:2012 - 8.6.6 Edit List Box
ISOBox.prototype._boxParsers['elst'] = function() {
  this._parseFullBox();
  this.entry_count = this._readUint(32);
  this.entries = [];

  for (var i=1; i <= this.entry_count; i++) {
    var entry = {};
    if (this.version == 1) {
      entry.segment_duration  = this._readUint(64);
      entry.media_time        = this._readInt(64);
    } else {
      entry.segment_duration  = this._readUint(32);
      entry.media_time        = this._readInt(32);
    }
    entry.media_rate_integer  = this._readInt(16);
    entry.media_rate_fraction = this._readInt(16);
    this.entries.push(entry);
  }
};;
// ISO/IEC 23009-1:2014 - 5.10.3.3 Event Message Box
ISOBox.prototype._boxParsers['emsg'] = function() {
  this._parseFullBox();
  this.scheme_id_uri           = this._readTerminatedString();
  this.value                   = this._readTerminatedString();
  this.timescale               = this._readUint(32);
  this.presentation_time_delta = this._readUint(32);
  this.event_duration          = this._readUint(32);
  this.id                      = this._readUint(32);
  this.message_data            = new DataView(this._raw.buffer, this._cursor.offset, this._raw.byteLength - (this._cursor.offset - this._offset));
};;
// ISO/IEC 14496-12:2012 - 8.1.2 Free Space Box
ISOBox.prototype._boxParsers['free'] = ISOBox.prototype._boxParsers['skip'] = function() {
  this.data = new DataView(this._raw.buffer, this._cursor.offset, this._raw.byteLength - (this._cursor.offset - this._offset));
};;
// ISO/IEC 14496-12:2012 - 4.3 File Type Box / 8.16.2 Segment Type Box
ISOBox.prototype._boxParsers['ftyp'] = ISOBox.prototype._boxParsers['styp'] = function() {
  this.major_brand = this._readString(4);
  this.minor_version = this._readUint(32);
  this.compatible_brands = [];

  while (this._cursor.offset - this._raw.byteOffset < this._raw.byteLength) {
    this.compatible_brands.push(this._readString(4));
  }
};;
// ISO/IEC 14496-12:2012 - 8.4.3 Handler Reference Box
ISOBox.prototype._boxParsers['hdlr'] = function() {
  this._parseFullBox();
  this.pre_defined = this._readUint(32);
  this.handler_type = this._readString(4);
  this.reserved = [this._readUint(32), this._readUint(32), this._readUint(32)];
  this.name = this._readTerminatedString();
};;
// ISO/IEC 14496-12:2012 - 8.1.1 Media Data Box
ISOBox.prototype._boxParsers['mdat'] = function() {
  this.data = this._readData();
};;
// ISO/IEC 14496-12:2012 - 8.4.2 Media Header Box
ISOBox.prototype._boxParsers['mdhd'] = function() {
  this._parseFullBox();
  if (this.version == 1) {
    this.creation_time = this._readUint(64);
    this.modification_time = this._readUint(64);
    this.timescale = this._readUint(32);
    this.duration = this._readUint(64);
  } else {
    this.creation_time = this._readUint(32);
    this.modification_time = this._readUint(32);
    this.timescale = this._readUint(32);
    this.duration = this._readUint(32);
  }
  var language = this._readUint(16);
  this.pad = (language >> 15);
  this.language = String.fromCharCode(
    ((language >> 10) & 0x1F) + 0x60,
    ((language >> 5) & 0x1F) + 0x60,
    (language & 0x1F) + 0x60
  );
  this.pre_defined = this._readUint(16);
};;
// ISO/IEC 14496-12:2012 - 8.8.2 Movie Extends Header Box
ISOBox.prototype._boxParsers['mehd'] = function() {
  this._parseFullBox();

  if (this.version == 1) {
    this.fragment_duration = this._readUint(64);
   } else { // version==0
    this.fragment_duration = this._readUint(32);
   }
};;
// ISO/IEC 14496-12:2012 - 8.8.5 Movie Fragment Header Box
ISOBox.prototype._boxParsers['mfhd'] = function() {
  this._parseFullBox();
  this.sequence_number = this._readUint(32);
};;
// ISO/IEC 14496-12:2012 - 8.8.11 Movie Fragment Random Access Box
ISOBox.prototype._boxParsers['mfro'] = function() {
  this._parseFullBox();
  this.mfra_size = this._readUint(32); // Called mfra_size to distinguish from the normal "size" attribute of a box
};
;
// ISO/IEC 14496-12:2012 - 8.5.2.2 mp4a box (use AudioSampleEntry definition and naming)
ISOBox.prototype._boxParsers['mp4a'] = function() {
  // SampleEntry fields
  this.reserved1 = [
    this._readUint(8),
    this._readUint(8),
    this._readUint(8),
    this._readUint(8),
    this._readUint(8),
    this._readUint(8)
  ];
  this.data_reference_index = this._readUint(16);
  // AudioSampleEntry fields
  this.reserved2    = [this._readUint(32), this._readUint(32)];
  this.channelcount = this._readUint(16);
  this.samplesize   = this._readUint(16);
  this.pre_defined  = this._readUint(16);
  this.reserved3    = this._readUint(16);
  this.samplerate   = this._readTemplate(32);
  // ESDescriptor fields
  this.esds = this._readData();
};;
// ISO/IEC 14496-12:2012 - 8.2.2 Movie Header Box
ISOBox.prototype._boxParsers['mvhd'] = function() {
  this._parseFullBox();
  var i;

  if (this.version == 1) {
    this.creation_time     = this._readUint(64);
    this.modification_time = this._readUint(64);
    this.timescale         = this._readUint(32);
    this.duration          = this._readUint(64);
  } else {
    this.creation_time     = this._readUint(32);
    this.modification_time = this._readUint(32);
    this.timescale         = this._readUint(32);
    this.duration          = this._readUint(32);
  }

  this.rate      = this._readTemplate(32);
  this.volume    = this._readTemplate(16);
  this.reserved1 = this._readUint(16);
  this.reserved2 = [ this._readUint(32), this._readUint(32) ];
  this.matrix = [];
  for (i=0; i<9; i++) {
    this.matrix.push(this._readTemplate(32));
  }
  this.pre_defined = [];
  for (i=0; i<6; i++) {
    this.pre_defined.push(this._readUint(32));
  }
  this.next_track_ID = this._readUint(32);
};;
// ISO/IEC 14496-30:2014 - WebVTT Cue Payload Box.
ISOBox.prototype._boxParsers['payl'] = function() {
  var cue_text_raw = new DataView(this._raw.buffer, this._cursor.offset, this._raw.byteLength - (this._cursor.offset - this._offset));
  this.cue_text = ISOBoxer.Utils.dataViewToString(cue_text_raw);
};
;
// ISO/IEC 14496-12:2012 - 8.16.3 Segment Index Box
ISOBox.prototype._boxParsers['sidx'] = function() {
  this._parseFullBox();
  this.reference_ID = this._readUint(32);
  this.timescale = this._readUint(32);
  if (this.version === 0) {
    this.earliest_presentation_time = this._readUint(32);
    this.first_offset = this._readUint(32);
  } else {
    this.earliest_presentation_time = this._readUint(64);
    this.first_offset = this._readUint(64);
  }
  this.reserved = this._readUint(16);
  this.reference_count = this._readUint(16);
  this.references = [];
  for (var i=0; i<this.reference_count; i++) {
    var ref = {};
    var reference = this._readUint(32);
    ref.reference_type = (reference >> 31) & 0x1;
    ref.referenced_size = reference & 0x7FFFFFFF;
    ref.subsegment_duration = this._readUint(32);
    var sap = this._readUint(32);
    ref.starts_with_SAP = (sap >> 31) & 0x1;
    ref.SAP_type = (sap >> 28) & 0x7;
    ref.SAP_delta_time = sap & 0xFFFFFFF;
    this.references.push(ref);
  }
};;
// ISO/IEC 14496-12:2012 - 8.16.4 Subsegment Index Box
ISOBox.prototype._boxParsers['ssix'] = function() {
  this._parseFullBox();
  this.subsegment_count = this._readUint(32);
  this.subsegments = [];

  for (var i=0; i<this.subsegment_count; i++) {
    var subsegment = {};
    subsegment.ranges_count = this._readUint(32);
    subsegment.ranges = [];

    for (var j=0; j<subsegment.ranges_count; j++) {
      var range = {};
      range.level = this._readUint(8);
      range.range_size = this._readUint(24);
      subsegment.ranges.push(range);
    }
    this.subsegments.push(subsegment);
  }
};;
// ISO/IEC 14496-12:2012 - 8.5.2 Sample Description Box
ISOBox.prototype._boxParsers['stsd'] = function() {
  this._parseFullBox();
  this.entry_count = this._readUint(32);
  this.entries = [];

  for (var i = 0; i < this.entry_count ; i++){
    this.entries.push(ISOBox.parse(this));
  }
};
;
// ISO/IEC 14496-12:2015 - 8.7.7 Sub-Sample Information Box
ISOBox.prototype._boxParsers['subs'] = function () {
  this._parseFullBox();
  this.entry_count = this._readUint(32);
  this.samples_with_subsamples = [];
  var sample_nr = 0;
  for (var i = 0; i < this.entry_count; i++) {
    var sample_delta = this._readUint(32);
    sample_nr += sample_delta;
    var subsample_count = this._readUint(16);
    if (subsample_count > 0) {
      var sample = {'nr': sample_nr, 'subsamples': [] };
      for (var j = 0; j < subsample_count; j++) {
        var subsample = {};
        if (this.version & 0x1) {
          subsample.size = this._readUint(32);
        } else {
          subsample.size = this._readUint(16);
        }
        subsample.priority = this._readUint(8);
        subsample.discardable = this._readUint(8);
        subsample.codec_specific_parameters = this._readUint(32);
        sample.subsamples.push(subsample);
      }
      this.samples_with_subsamples.push(sample);
    }
  }
};;
// ISO/IEC 14496-12:2012 - 8.8.12 Track Fragmnent Decode Time
ISOBox.prototype._boxParsers['tfdt'] = function() {
  this._parseFullBox();
  if (this.version == 1) {
    this.baseMediaDecodeTime = this._readUint(64);
  } else {
    this.baseMediaDecodeTime = this._readUint(32);
  }
};;
// ISO/IEC 14496-12:2012 - 8.8.7 Track Fragment Header Box
ISOBox.prototype._boxParsers['tfhd'] = function() {
  this._parseFullBox();
  this.track_ID = this._readUint(32);
  if (this.flags & 0x1) this.base_data_offset = this._readUint(64);
  if (this.flags & 0x2) this.sample_description_offset = this._readUint(32);
  if (this.flags & 0x8) this.default_sample_duration = this._readUint(32);
  if (this.flags & 0x10) this.default_sample_size = this._readUint(32);
  if (this.flags & 0x20) this.default_sample_flags = this._readUint(32);
};;
// ISO/IEC 14496-12:2012 - 8.8.10 Track Fragment Random Access Box
ISOBox.prototype._boxParsers['tfra'] = function() {
  this._parseFullBox();
  this.track_ID = this._readUint(32);
  this._packed = this._readUint(32);

  this.reserved = this._packed >>> 6;

  this.length_size_of_traf_num = (this._packed && 0xFFFF00000000) >>> 4;
  this.length_size_of_trun_num = (this._packed && 0xFFFF0000) >>> 2;
  this.length_size_of_sample_num = this._packed && 0xFF;

  this.number_of_entry = this._readUint(32);

  this.entries = [];

  for (var i = 0; i < this.number_of_entry ; i++){
    var entry = {};

    if(this.version==1){
      entry.time = this._readUint(64);
      entry.moof_offset = this._readUint(64);
    }else{
      entry.time = this._readUint(32);
      entry.moof_offset = this._readUint(32);
    }

    entry.traf_number = this._readUint((this.length_size_of_traf_num + 1) * 8);
    entry.trun_number = this._readUint((this.length_size_of_trun_num + 1) * 8);
    entry.sample_number = this._readUint((this.length_size_of_sample_num + 1) * 8);

    this.entries.push(entry);
  }
};
;
// ISO/IEC 14496-12:2012 - 8.3.2 Track Header Box
ISOBox.prototype._boxParsers['tkhd'] = function() {
  this._parseFullBox();

  if (this.version == 1) {
    this.creation_time     = this._readUint(64);
    this.modification_time = this._readUint(64);
    this.track_ID          = this._readUint(32);
    this.reserved1         = this._readUint(32);
    this.duration          = this._readUint(64);
  } else {
    this.creation_time     = this._readUint(32);
    this.modification_time = this._readUint(32);
    this.track_ID          = this._readUint(32);
    this.reserved1         = this._readUint(32);
    this.duration          = this._readUint(32);
  }

  this.reserved2 = [
    this._readUint(32),
    this._readUint(32)
  ];
  this.layer = this._readUint(16);
  this.alternate_group = this._readUint(16);
  this.volume = this._readTemplate(16);
  this.reserved3 = this._readUint(16);
  this.matrix = [];
  for (var i=0; i<9; i++) {
    this.matrix.push(this._readTemplate(32));
  }
  this.width = this._readTemplate(32);
  this.height = this._readTemplate(32);
};;
// ISO/IEC 14496-12:2012 - 8.8.3 Track Extends Box
ISOBox.prototype._boxParsers['trex'] = function() {
  this._parseFullBox();

  this.track_ID = this._readUint(32);
  this.default_sample_description_index = this._readUint(32);
  this.default_sample_duration = this._readUint(32);
  this.default_sample_size = this._readUint(32);
  this.default_sample_flags = this._readUint(32);
};;
// ISO/IEC 14496-12:2012 - 8.8.8 Track Run Box
// Note: the 'trun' box has a direct relation to the 'tfhd' box for defaults.
// These defaults are not set explicitly here, but are left to resolve for the user.
ISOBox.prototype._boxParsers['trun'] = function() {
  this._parseFullBox();
  this.sample_count = this._readUint(32);
  if (this.flags & 0x1) this.data_offset = this._readInt(32);
  if (this.flags & 0x4) this.first_sample_flags = this._readUint(32);
  this.samples = [];
  for (var i=0; i<this.sample_count; i++) {
    var sample = {};
    if (this.flags & 0x100) sample.sample_duration = this._readUint(32);
    if (this.flags & 0x200) sample.sample_size = this._readUint(32);
    if (this.flags & 0x400) sample.sample_flags = this._readUint(32);
    if (this.flags & 0x800) {
      if (this.version === 0) {
        sample.sample_composition_time_offset = this._readUint(32);
      } else {
        sample.sample_composition_time_offset = this._readInt(32);
      }
    }
    this.samples.push(sample);
  }
};;
// ISO/IEC 14496-30:2014 - WebVTT Source Label Box
ISOBox.prototype._boxParsers['vlab'] = function() {
  var source_label_raw = new DataView(this._raw.buffer, this._cursor.offset, this._raw.byteLength - (this._cursor.offset - this._offset));
  this.source_label = ISOBoxer.Utils.dataViewToString(source_label_raw);
};
;
// ISO/IEC 14496-30:2014 - WebVTT Configuration Box
ISOBox.prototype._boxParsers['vttC'] = function() {
  var config_raw = new DataView(this._raw.buffer, this._cursor.offset, this._raw.byteLength - (this._cursor.offset - this._offset));
  this.config = ISOBoxer.Utils.dataViewToString(config_raw);
};
;
// ISO/IEC 14496-30:2014 - WebVTT Empty Sample Box
ISOBox.prototype._boxParsers['vtte'] = function() {
  // Nothing should happen here.
};

},{}],5:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _EventBusJs = _dereq_('./EventBus.js');

var _EventBusJs2 = _interopRequireDefault(_EventBusJs);

var _eventsEventsJs = _dereq_('./events/Events.js');

var _eventsEventsJs2 = _interopRequireDefault(_eventsEventsJs);

var _FactoryMakerJs = _dereq_('./FactoryMaker.js');

var _FactoryMakerJs2 = _interopRequireDefault(_FactoryMakerJs);

/**
 * @module Debug
 */
function Debug() {

    var context = this.context;
    var eventBus = (0, _EventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        logToBrowserConsole = undefined,
        showLogTimestamp = undefined,
        startTime = undefined;

    function setup() {
        logToBrowserConsole = true;
        showLogTimestamp = true;
        startTime = new Date().getTime();
    }

    /**
     * Prepends a timestamp in milliseconds to each log message.
     * @param {boolean} value Set to true if you want to see a timestamp in each log message.
     * @default false
     * @memberof module:Debug
     * @instance
     */
    function setLogTimestampVisible(value) {
        showLogTimestamp = value;
    }
    /**
     * Toggles logging to the browser's javascript console.  If you set to false you will still receive a log event with the same message.
     * @param {boolean} value Set to false if you want to turn off logging to the browser's console.
     * @default true
     * @memberof module:Debug
     * @instance
     */
    function setLogToBrowserConsole(value) {
        logToBrowserConsole = value;
    }
    /**
     * Use this method to get the state of logToBrowserConsole.
     * @returns {boolean} The current value of logToBrowserConsole
     * @memberof module:Debug
     * @instance
     */
    function getLogToBrowserConsole() {
        return logToBrowserConsole;
    }
    /**
     * This method will allow you send log messages to either the browser's console and/or dispatch an event to capture at the media player level.
     * @param arguments The message you want to log. The Arguments object is supported for this method so you can send in comma separated logging items.
     * @memberof module:Debug
     * @instance
     */
    function log() {

        var message = '';
        var logTime = null;

        if (showLogTimestamp) {
            logTime = new Date().getTime();
            message += '[' + (logTime - startTime) + ']';
        }

        if (message.length > 0) {
            message += ' ';
        }

        Array.apply(null, arguments).forEach(function (item) {
            message += item + ' ';
        });

        if (logToBrowserConsole) {
            console.log(message);
        }

        eventBus.trigger(_eventsEventsJs2['default'].LOG, { message: message });
    }

    instance = {
        log: log,
        setLogTimestampVisible: setLogTimestampVisible,
        setLogToBrowserConsole: setLogToBrowserConsole,
        getLogToBrowserConsole: getLogToBrowserConsole
    };

    setup();

    return instance;
}

Debug.__dashjs_factory_name = 'Debug';
exports['default'] = _FactoryMakerJs2['default'].getSingletonFactory(Debug);
module.exports = exports['default'];

},{"./EventBus.js":6,"./FactoryMaker.js":7,"./events/Events.js":9}],6:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _FactoryMakerJs = _dereq_('./FactoryMaker.js');

var _FactoryMakerJs2 = _interopRequireDefault(_FactoryMakerJs);

function EventBus() {

    var instance = undefined;
    var handlers = {};

    function on(type, listener, scope) {
        if (!type) {
            throw new Error('event type cannot be null or undefined');
        }

        if (!listener || typeof listener !== 'function') {
            throw new Error('listener must be a function: ' + listener);
        }

        if (getHandlerIdx(type, listener, scope) >= 0) return;

        var handler = {
            callback: listener,
            scope: scope
        };

        handlers[type] = handlers[type] || [];
        handlers[type].push(handler);
    }

    function off(type, listener, scope) {
        if (!type || !listener || !handlers[type]) return;

        var idx = getHandlerIdx(type, listener, scope);

        if (idx < 0) return;

        handlers[type].splice(idx, 1);
    }

    function trigger(type, args) {
        if (!type || !handlers[type]) return;

        args = args || {};

        if (args.hasOwnProperty('type')) {
            throw new Error('\'type\' is a reserved word for event dispatching');
        }

        args.type = type;

        handlers[type].forEach(function (handler) {
            handler.callback.call(handler.scope, args);
        });
    }

    function reset() {
        handlers = {};
    }

    function getHandlerIdx(type, listener, scope) {
        var handlersForType = handlers[type];
        var result = -1;

        if (!handlersForType || handlersForType.length === 0) return result;

        for (var i = 0; i < handlersForType.length; i++) {
            if (handlersForType[i].callback === listener && (!scope || scope === handlersForType[i].scope)) return i;
        }

        return result;
    }

    instance = {
        on: on,
        off: off,
        trigger: trigger,
        reset: reset
    };

    return instance;
}

EventBus.__dashjs_factory_name = 'EventBus';
exports['default'] = _FactoryMakerJs2['default'].getSingletonFactory(EventBus);
module.exports = exports['default'];

},{"./FactoryMaker.js":7}],7:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @Module FactoryMaker
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
    value: true
});
var FactoryMaker = (function () {

    var instance = undefined;
    var extensions = [];
    var singletonContexts = [];

    function extend(name, childInstance, override, context) {
        var extensionContext = getExtensionContext(context);
        if (!extensionContext[name] && childInstance) {
            extensionContext[name] = { instance: childInstance, override: override };
        }
    }

    /**
     * Use this method from your extended object.  this.factory is injected into your object.
     * this.factory.getSingletonInstance(this.context, 'VideoModel')
     * will return the video model for use in the extended object.
     *
     * @param context {Object} injected into extended object as this.context
     * @param className {String} string name found in all dash.js objects
     * with name __dashjs_factory_name Will be at the bottom. Will be the same as the object's name.
     * @returns {*} Context aware instance of specified singleton name.
     * @memberof module:FactoryMaker
     * @instance
     */
    function getSingletonInstance(context, className) {
        for (var i in singletonContexts) {
            var obj = singletonContexts[i];
            if (obj.context === context && obj.name === className) {
                return obj.instance;
            }
        }
        return null;
    }

    /**
     * Use this method to add an singleton instance to the system.  Useful for unit testing to mock objects etc.
     *
     * @param context
     * @param className
     * @param instance
     * @memberof module:FactoryMaker
     * @instance
     */
    function setSingletonInstance(context, className, instance) {
        for (var i in singletonContexts) {
            var obj = singletonContexts[i];
            if (obj.context === context && obj.name === className) {
                singletonContexts[i].instance = instance;
                return;
            }
        }
        singletonContexts.push({ name: className, context: context, instance: instance });
    }

    function getClassFactory(classConstructor) {
        return function (context) {
            if (context === undefined) {
                context = {};
            }
            return {
                create: function create() {
                    return merge(classConstructor.__dashjs_factory_name, classConstructor.apply({ context: context }, arguments), context, arguments);
                }
            };
        };
    }

    function getSingletonFactory(classConstructor) {
        return function (context) {
            var instance = undefined;
            if (context === undefined) {
                context = {};
            }
            return {
                getInstance: function getInstance() {
                    // If we don't have an instance yet check for one on the context
                    if (!instance) {
                        instance = getSingletonInstance(context, classConstructor.__dashjs_factory_name);
                    }
                    // If there's no instance on the context then create one
                    if (!instance) {
                        instance = merge(classConstructor.__dashjs_factory_name, classConstructor.apply({ context: context }, arguments), context, arguments);
                        singletonContexts.push({ name: classConstructor.__dashjs_factory_name, context: context, instance: instance });
                    }
                    return instance;
                }
            };
        };
    }

    function merge(name, classConstructor, context, args) {
        var extensionContext = getExtensionContext(context);
        var extensionObject = extensionContext[name];
        if (extensionObject) {
            var extension = extensionObject.instance;
            if (extensionObject.override) {
                //Override public methods in parent but keep parent.
                extension = extension.apply({ context: context, factory: instance, parent: classConstructor }, args);
                for (var prop in extension) {
                    if (classConstructor.hasOwnProperty(prop)) {
                        classConstructor[prop] = extension[prop];
                    }
                }
            } else {
                //replace parent object completely with new object. Same as dijon.
                return extension.apply({ context: context, factory: instance }, args);
            }
        }
        return classConstructor;
    }

    function getExtensionContext(context) {
        var extensionContext = undefined;
        extensions.forEach(function (obj) {
            if (obj === context) {
                extensionContext = obj;
            }
        });
        if (!extensionContext) {
            extensionContext = extensions.push(context);
        }
        return extensionContext;
    }

    instance = {
        extend: extend,
        getSingletonInstance: getSingletonInstance,
        setSingletonInstance: setSingletonInstance,
        getSingletonFactory: getSingletonFactory,
        getClassFactory: getClassFactory
    };

    return instance;
})();

exports["default"] = FactoryMaker;
module.exports = exports["default"];

},{}],8:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

var _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); } } };

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _EventsBaseJs = _dereq_('./EventsBase.js');

var _EventsBaseJs2 = _interopRequireDefault(_EventsBaseJs);

/**
 * @class
 * @ignore
 */

var CoreEvents = (function (_EventsBase) {
    _inherits(CoreEvents, _EventsBase);

    function CoreEvents() {
        _classCallCheck(this, CoreEvents);

        _get(Object.getPrototypeOf(CoreEvents.prototype), 'constructor', this).call(this);
        this.AST_IN_FUTURE = 'astinfuture';
        this.BUFFERING_COMPLETED = 'bufferingCompleted';
        this.BUFFER_CLEARED = 'bufferCleared';
        this.BUFFER_LEVEL_UPDATED = 'bufferLevelUpdated';
        this.BYTES_APPENDED = 'bytesAppended';
        this.CHECK_FOR_EXISTENCE_COMPLETED = 'checkForExistenceCompleted';
        this.CHUNK_APPENDED = 'chunkAppended';
        this.CURRENT_TRACK_CHANGED = 'currenttrackchanged';
        this.DATA_UPDATE_COMPLETED = 'dataUpdateCompleted';
        this.DATA_UPDATE_STARTED = 'dataUpdateStarted';
        this.FRAGMENT_LOADING_COMPLETED = 'fragmentLoadingCompleted';
        this.FRAGMENT_LOADING_STARTED = 'fragmentLoadingStarted';
        this.INITIALIZATION_LOADED = 'initializationLoaded';
        this.INIT_FRAGMENT_LOADED = 'initFragmentLoaded';
        this.INIT_REQUESTED = 'initRequested';
        this.INTERNAL_MANIFEST_LOADED = 'internalManifestLoaded';
        this.LIVE_EDGE_SEARCH_COMPLETED = 'liveEdgeSearchCompleted';
        this.LOADING_COMPLETED = 'loadingCompleted';
        this.LOADING_PROGRESS = 'loadingProgress';
        this.MANIFEST_UPDATED = 'manifestUpdated';
        this.MEDIA_FRAGMENT_LOADED = 'mediaFragmentLoaded';
        this.QUALITY_CHANGED = 'qualityChanged';
        this.QUOTA_EXCEEDED = 'quotaExceeded';
        this.REPRESENTATION_UPDATED = 'representationUpdated';
        this.SEGMENTS_LOADED = 'segmentsLoaded';
        this.SERVICE_LOCATION_BLACKLIST_CHANGED = 'serviceLocationBlacklistChanged';
        this.SOURCEBUFFER_APPEND_COMPLETED = 'sourceBufferAppendCompleted';
        this.SOURCEBUFFER_REMOVE_COMPLETED = 'sourceBufferRemoveCompleted';
        this.STREAMS_COMPOSED = 'streamsComposed';
        this.STREAM_BUFFERING_COMPLETED = 'streamBufferingCompleted';
        this.STREAM_COMPLETED = 'streamCompleted';
        this.STREAM_INITIALIZED = 'streaminitialized';
        this.STREAM_TEARDOWN_COMPLETE = 'streamTeardownComplete';
        this.TIMED_TEXT_REQUESTED = 'timedTextRequested';
        this.TIME_SYNCHRONIZATION_COMPLETED = 'timeSynchronizationComplete';
        this.URL_RESOLUTION_FAILED = 'urlResolutionFailed';
        this.WALLCLOCK_TIME_UPDATED = 'wallclockTimeUpdated';
        this.XLINK_ALL_ELEMENTS_LOADED = 'xlinkAllElementsLoaded';
        this.XLINK_ELEMENT_LOADED = 'xlinkElementLoaded';
        this.XLINK_READY = 'xlinkReady';
    }

    return CoreEvents;
})(_EventsBaseJs2['default']);

exports['default'] = CoreEvents;
module.exports = exports['default'];

},{"./EventsBase.js":10}],9:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _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); } } };

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _CoreEventsJs = _dereq_('./CoreEvents.js');

var _CoreEventsJs2 = _interopRequireDefault(_CoreEventsJs);

var Events = (function (_CoreEvents) {
  _inherits(Events, _CoreEvents);

  function Events() {
    _classCallCheck(this, Events);

    _get(Object.getPrototypeOf(Events.prototype), 'constructor', this).apply(this, arguments);
  }

  return Events;
})(_CoreEventsJs2['default']);

var events = new Events();
exports['default'] = events;
module.exports = exports['default'];

},{"./CoreEvents.js":8}],10:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

var _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; }; })();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var EventsBase = (function () {
    function EventsBase() {
        _classCallCheck(this, EventsBase);
    }

    _createClass(EventsBase, [{
        key: 'extend',
        value: function extend(events, config) {
            if (!events) return;

            var override = config ? config.override : false;
            var publicOnly = config ? config.publicOnly : false;

            for (var evt in events) {
                if (!events.hasOwnProperty(evt) || this[evt] && !override) continue;
                if (publicOnly && events[evt].indexOf('public_') === -1) continue;
                this[evt] = events[evt];
            }
        }
    }]);

    return EventsBase;
})();

exports['default'] = EventsBase;
module.exports = exports['default'];

},{}],11:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _streamingVoTrackInfoJs = _dereq_('../streaming/vo/TrackInfo.js');

var _streamingVoTrackInfoJs2 = _interopRequireDefault(_streamingVoTrackInfoJs);

var _streamingVoMediaInfoJs = _dereq_('../streaming/vo/MediaInfo.js');

var _streamingVoMediaInfoJs2 = _interopRequireDefault(_streamingVoMediaInfoJs);

var _streamingVoStreamInfoJs = _dereq_('../streaming/vo/StreamInfo.js');

var _streamingVoStreamInfoJs2 = _interopRequireDefault(_streamingVoStreamInfoJs);

var _streamingVoManifestInfoJs = _dereq_('../streaming/vo/ManifestInfo.js');

var _streamingVoManifestInfoJs2 = _interopRequireDefault(_streamingVoManifestInfoJs);

var _voEventJs = _dereq_('./vo/Event.js');

var _voEventJs2 = _interopRequireDefault(_voEventJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _externalsCea608ParserJs = _dereq_('../../externals/cea608-parser.js');

var _externalsCea608ParserJs2 = _interopRequireDefault(_externalsCea608ParserJs);

var METRIC_LIST = {
    //TODO need to refactor all that reference to be able to export like all other const on factory object.
    TCP_CONNECTION: 'TcpList',
    HTTP_REQUEST: 'HttpList',
    TRACK_SWITCH: 'RepSwitchList',
    BUFFER_LEVEL: 'BufferLevel',
    BUFFER_STATE: 'BufferState',
    DVR_INFO: 'DVRInfo',
    DROPPED_FRAMES: 'DroppedFrames',
    SCHEDULING_INFO: 'SchedulingInfo',
    REQUESTS_QUEUE: 'RequestsQueue',
    MANIFEST_UPDATE: 'ManifestUpdate',
    MANIFEST_UPDATE_STREAM_INFO: 'ManifestUpdatePeriodInfo',
    MANIFEST_UPDATE_TRACK_INFO: 'ManifestUpdateRepresentationInfo',
    PLAY_LIST: 'PlayList',
    DVB_ERRORS: 'DVBErrors'
};

function DashAdapter() {

    //let context = this.context;

    var instance = undefined,
        dashManifestModel = undefined,
        periods = undefined,
        adaptations = undefined;

    function setConfig(config) {
        if (!config) return;

        if (config.dashManifestModel) {
            dashManifestModel = config.dashManifestModel;
        }
    }

    function initialize() {
        periods = [];
        adaptations = {};
    }

    function getRepresentationForTrackInfo(trackInfo, representationController) {
        return representationController.getRepresentationForQuality(trackInfo.quality);
    }

    function getAdaptationForMediaInfo(mediaInfo) {
        return adaptations[mediaInfo.streamInfo.id][mediaInfo.index];
    }

    function getPeriodForStreamInfo(streamInfo) {
        var ln = periods.length;

        for (var i = 0; i < ln; i++) {
            var period = periods[i];

            if (streamInfo.id === period.id) return period;
        }

        return null;
    }

    function convertRepresentationToTrackInfo(manifest, representation) {
        var trackInfo = new _streamingVoTrackInfoJs2['default']();
        var a = representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index];
        var r = dashManifestModel.getRepresentationFor(representation.index, a);

        trackInfo.id = representation.id;
        trackInfo.quality = representation.index;
        trackInfo.bandwidth = dashManifestModel.getBandwidth(r);
        trackInfo.DVRWindow = representation.segmentAvailabilityRange;
        trackInfo.fragmentDuration = representation.segmentDuration || (representation.segments && representation.segments.length > 0 ? representation.segments[0].duration : NaN);
        trackInfo.MSETimeOffset = representation.MSETimeOffset;
        trackInfo.useCalculatedLiveEdgeTime = representation.useCalculatedLiveEdgeTime;
        trackInfo.mediaInfo = convertAdaptationToMediaInfo(manifest, representation.adaptation);

        return trackInfo;
    }

    function convertAdaptationToMediaInfo(manifest, adaptation) {
        var mediaInfo = new _streamingVoMediaInfoJs2['default']();
        var a = adaptation.period.mpd.manifest.Period_asArray[adaptation.period.index].AdaptationSet_asArray[adaptation.index];
        var viewpoint;

        mediaInfo.id = adaptation.id;
        mediaInfo.index = adaptation.index;
        mediaInfo.type = adaptation.type;
        mediaInfo.streamInfo = convertPeriodToStreamInfo(manifest, adaptation.period);
        mediaInfo.representationCount = dashManifestModel.getRepresentationCount(a);
        mediaInfo.lang = dashManifestModel.getLanguageForAdaptation(a);
        viewpoint = dashManifestModel.getViewpointForAdaptation(a);
        mediaInfo.viewpoint = viewpoint ? viewpoint.value : undefined;
        mediaInfo.accessibility = dashManifestModel.getAccessibilityForAdaptation(a).map(function (accessibility) {
            var accessibilityValue = accessibility.value;
            var accessibilityData = accessibilityValue;
            if (accessibility.schemeIdUri && accessibility.schemeIdUri.search('cea-608') >= 0 && typeof _externalsCea608ParserJs2['default'] !== 'undefined') {
                if (accessibilityValue) {
                    accessibilityData = 'cea-608:' + accessibilityValue;
                } else {
                    accessibilityData = 'cea-608';
                }
                mediaInfo.embeddedCaptions = true;
            }
            return accessibilityData;
        });
        mediaInfo.audioChannelConfiguration = dashManifestModel.getAudioChannelConfigurationForAdaptation(a).map(function (audioChannelConfiguration) {
            return audioChannelConfiguration.value;
        });
        mediaInfo.roles = dashManifestModel.getRolesForAdaptation(a).map(function (role) {
            return role.value;
        });
        mediaInfo.codec = dashManifestModel.getCodec(a);
        mediaInfo.mimeType = dashManifestModel.getMimeType(a);
        mediaInfo.contentProtection = dashManifestModel.getContentProtectionData(a);
        mediaInfo.bitrateList = dashManifestModel.getBitrateListForAdaptation(a);

        if (mediaInfo.contentProtection) {
            mediaInfo.contentProtection.forEach(function (item) {
                item.KID = dashManifestModel.getKID(item);
            });
        }

        mediaInfo.isText = dashManifestModel.getIsTextTrack(mediaInfo.mimeType);

        return mediaInfo;
    }

    function convertVideoInfoToEmbeddedTextInfo(mediaInfo, channel, lang) {
        mediaInfo.id = channel; // CC1, CC2, CC3, or CC4
        mediaInfo.index = 100 + parseInt(channel.substring(2, 3));
        mediaInfo.type = 'embeddedText';
        mediaInfo.codec = 'cea-608-in-SEI';
        mediaInfo.isText = true;
        mediaInfo.isEmbedded = true;
        mediaInfo.lang = channel + ' ' + lang;
        mediaInfo.roles = ['caption'];
    }

    function convertPeriodToStreamInfo(manifest, period) {
        var streamInfo = new _streamingVoStreamInfoJs2['default']();
        var THRESHOLD = 1;

        streamInfo.id = period.id;
        streamInfo.index = period.index;
        streamInfo.start = period.start;
        streamInfo.duration = period.duration;
        streamInfo.manifestInfo = convertMpdToManifestInfo(manifest, period.mpd);
        streamInfo.isLast = manifest.Period_asArray.length === 1 || Math.abs(streamInfo.start + streamInfo.duration - streamInfo.manifestInfo.duration) < THRESHOLD;
        streamInfo.isFirst = manifest.Period_asArray.length === 1 || dashManifestModel.getRegularPeriods(manifest, dashManifestModel.getMpd(manifest))[0].id === period.id;

        return streamInfo;
    }

    function convertMpdToManifestInfo(manifest, mpd) {
        var manifestInfo = new _streamingVoManifestInfoJs2['default']();

        manifestInfo.DVRWindowSize = mpd.timeShiftBufferDepth;
        manifestInfo.loadedTime = mpd.manifest.loadedTime;
        manifestInfo.availableFrom = mpd.availabilityStartTime;
        manifestInfo.minBufferTime = mpd.manifest.minBufferTime;
        manifestInfo.maxFragmentDuration = mpd.maxSegmentDuration;
        manifestInfo.duration = dashManifestModel.getDuration(manifest);
        manifestInfo.isDynamic = dashManifestModel.getIsDynamic(manifest);

        return manifestInfo;
    }

    function getMediaInfoForType(manifest, streamInfo, type) {
        var periodInfo = getPeriodForStreamInfo(streamInfo);
        var periodId = periodInfo.id;
        var data = dashManifestModel.getAdaptationForType(manifest, streamInfo.index, type);
        var idx;

        if (!data) return null;

        idx = dashManifestModel.getIndexForAdaptation(data, manifest, streamInfo.index);

        adaptations[periodId] = adaptations[periodId] || dashManifestModel.getAdaptationsForPeriod(manifest, periodInfo);

        return convertAdaptationToMediaInfo(manifest, adaptations[periodId][idx]);
    }

    function getAllMediaInfoForType(manifest, streamInfo, type) {
        var periodInfo = getPeriodForStreamInfo(streamInfo);
        var periodId = periodInfo.id;
        var adaptationsForType = dashManifestModel.getAdaptationsForType(manifest, streamInfo.index, type !== 'embeddedText' ? type : 'video');

        var mediaArr = [];

        var data, media, idx, i, j, ln;

        if (!adaptationsForType) return mediaArr;

        adaptations[periodId] = adaptations[periodId] || dashManifestModel.getAdaptationsForPeriod(manifest, periodInfo);

        for (i = 0, ln = adaptationsForType.length; i < ln; i++) {
            data = adaptationsForType[i];
            idx = dashManifestModel.getIndexForAdaptation(data, manifest, streamInfo.index);
            media = convertAdaptationToMediaInfo(manifest, adaptations[periodId][idx]);

            if (type === 'embeddedText') {
                var accessibilityLength = media.accessibility.length;
                for (j = 0; j < accessibilityLength; j++) {
                    if (!media) {
                        continue;
                    }
                    var accessibility = media.accessibility[j];
                    if (accessibility.indexOf('cea-608:') === 0) {
                        var value = accessibility.substring(8);
                        var parts = value.split(';');
                        if (parts[0].substring(0, 2) === 'CC') {
                            for (j = 0; j < parts.length; j++) {
                                if (!media) {
                                    media = convertAdaptationToMediaInfo.call(this, manifest, adaptations[periodId][idx]);
                                }
                                convertVideoInfoToEmbeddedTextInfo(media, parts[j].substring(0, 3), parts[j].substring(4));
                                mediaArr.push(media);
                                media = null;
                            }
                        } else {
                            for (j = 0; j < parts.length; j++) {
                                // Only languages for CC1, CC2, ...
                                if (!media) {
                                    media = convertAdaptationToMediaInfo.call(this, manifest, adaptations[periodId][idx]);
                                }
                                convertVideoInfoToEmbeddedTextInfo(media, 'CC' + (j + 1), parts[j]);
                                mediaArr.push(media);
                                media = null;
                            }
                        }
                    } else if (accessibility.indexOf('cea-608') === 0) {
                        // Nothing known. We interpret it as CC1=eng
                        convertVideoInfoToEmbeddedTextInfo(media, 'CC1', 'eng');
                        mediaArr.push(media);
                        media = null;
                    }
                }
            }
            if (media && type !== 'embeddedText') {
                mediaArr.push(media);
            }
        }

        return mediaArr;
    }

    function getStreamsInfo(manifest) {
        var streams = [];
        var mpd, ln, i;

        if (!manifest) return null;

        mpd = dashManifestModel.getMpd(manifest);
        periods = dashManifestModel.getRegularPeriods(manifest, mpd);
        mpd.checkTime = dashManifestModel.getCheckTime(manifest, periods[0]);
        adaptations = {};
        ln = periods.length;

        for (i = 0; i < ln; i++) {
            streams.push(convertPeriodToStreamInfo(manifest, periods[i]));
        }

        return streams;
    }

    function getManifestInfo(manifest) {
        var mpd = dashManifestModel.getMpd(manifest);

        return convertMpdToManifestInfo(manifest, mpd);
    }

    function getInitRequest(streamProcessor, quality) {
        var representation = streamProcessor.getRepresentationController().getRepresentationForQuality(quality);
        return streamProcessor.getIndexHandler().getInitRequest(representation);
    }

    function getNextFragmentRequest(streamProcessor, trackInfo) {
        var representation = getRepresentationForTrackInfo(trackInfo, streamProcessor.getRepresentationController());
        return streamProcessor.getIndexHandler().getNextSegmentRequest(representation);
    }

    function getFragmentRequestForTime(streamProcessor, trackInfo, time, options) {
        var representation = getRepresentationForTrackInfo(trackInfo, streamProcessor.getRepresentationController());
        return streamProcessor.getIndexHandler().getSegmentRequestForTime(representation, time, options);
    }

    function generateFragmentRequestForTime(streamProcessor, trackInfo, time) {
        var representation = getRepresentationForTrackInfo(trackInfo, streamProcessor.getRepresentationController());
        return streamProcessor.getIndexHandler().generateSegmentRequestForTime(representation, time);
    }

    function getIndexHandlerTime(streamProcessor) {
        return streamProcessor.getIndexHandler().getCurrentTime();
    }

    function setIndexHandlerTime(streamProcessor, value) {
        return streamProcessor.getIndexHandler().setCurrentTime(value);
    }

    function updateData(manifest, streamProcessor) {
        var periodInfo = getPeriodForStreamInfo(streamProcessor.getStreamInfo());
        var mediaInfo = streamProcessor.getMediaInfo();
        var adaptation = getAdaptationForMediaInfo(mediaInfo);
        var type = streamProcessor.getType();

        var id, data;

        id = mediaInfo.id;
        data = id ? dashManifestModel.getAdaptationForId(id, manifest, periodInfo.index) : dashManifestModel.getAdaptationForIndex(mediaInfo.index, manifest, periodInfo.index);
        streamProcessor.getRepresentationController().updateData(data, adaptation, type);
    }

    function getRepresentationInfoForQuality(manifest, representationController, quality) {
        var representation = representationController.getRepresentationForQuality(quality);
        return representation ? convertRepresentationToTrackInfo(manifest, representation) : null;
    }

    function getCurrentRepresentationInfo(manifest, representationController) {
        var representation = representationController.getCurrentRepresentation();
        return representation ? convertRepresentationToTrackInfo(manifest, representation) : null;
    }

    function getEvent(eventBox, eventStreams, startTime) {
        var event = new _voEventJs2['default']();
        var schemeIdUri = eventBox.scheme_id_uri;
        var value = eventBox.value;
        var timescale = eventBox.timescale;
        var presentationTimeDelta = eventBox.presentation_time_delta;
        var duration = eventBox.event_duration;
        var id = eventBox.id;
        var messageData = eventBox.message_data;
        var presentationTime = startTime * timescale + presentationTimeDelta;

        if (!eventStreams[schemeIdUri]) return null;

        event.eventStream = eventStreams[schemeIdUri];
        event.eventStream.value = value;
        event.eventStream.timescale = timescale;
        event.duration = duration;
        event.id = id;
        event.presentationTime = presentationTime;
        event.messageData = messageData;
        event.presentationTimeDelta = presentationTimeDelta;

        return event;
    }

    function getEventsFor(manifest, info, streamProcessor) {
        var events = [];

        if (info instanceof _streamingVoStreamInfoJs2['default']) {
            events = dashManifestModel.getEventsForPeriod(manifest, getPeriodForStreamInfo(info));
        } else if (info instanceof _streamingVoMediaInfoJs2['default']) {
            events = dashManifestModel.getEventStreamForAdaptationSet(manifest, getAdaptationForMediaInfo(info));
        } else if (info instanceof _streamingVoTrackInfoJs2['default']) {
            events = dashManifestModel.getEventStreamForRepresentation(manifest, getRepresentationForTrackInfo(info, streamProcessor.getRepresentationController()));
        }

        return events;
    }

    function reset() {
        periods = [];
        adaptations = {};
    }

    instance = {
        initialize: initialize,
        convertDataToTrack: convertRepresentationToTrackInfo,
        convertDataToMedia: convertAdaptationToMediaInfo,
        convertDataToStream: convertPeriodToStreamInfo,
        getDataForTrack: getRepresentationForTrackInfo,
        getDataForMedia: getAdaptationForMediaInfo,
        getDataForStream: getPeriodForStreamInfo,
        getStreamsInfo: getStreamsInfo,
        getManifestInfo: getManifestInfo,
        getMediaInfoForType: getMediaInfoForType,
        getAllMediaInfoForType: getAllMediaInfoForType,
        getCurrentRepresentationInfo: getCurrentRepresentationInfo,
        getRepresentationInfoForQuality: getRepresentationInfoForQuality,
        updateData: updateData,
        getInitRequest: getInitRequest,
        getNextFragmentRequest: getNextFragmentRequest,
        getFragmentRequestForTime: getFragmentRequestForTime,
        generateFragmentRequestForTime: generateFragmentRequestForTime,
        getIndexHandlerTime: getIndexHandlerTime,
        setIndexHandlerTime: setIndexHandlerTime,
        getEventsFor: getEventsFor,
        getEvent: getEvent,
        setConfig: setConfig,
        reset: reset,
        metricsList: METRIC_LIST
    };

    return instance;
}

DashAdapter.__dashjs_factory_name = 'DashAdapter';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(DashAdapter);
module.exports = exports['default'];

},{"../../externals/cea608-parser.js":1,"../core/FactoryMaker.js":7,"../streaming/vo/ManifestInfo.js":104,"../streaming/vo/MediaInfo.js":105,"../streaming/vo/StreamInfo.js":107,"../streaming/vo/TrackInfo.js":110,"./vo/Event.js":27}],12:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _streamingVoFragmentRequestJs = _dereq_('../streaming/vo/FragmentRequest.js');

var _streamingVoFragmentRequestJs2 = _interopRequireDefault(_streamingVoFragmentRequestJs);

var _streamingVoErrorJs = _dereq_('../streaming/vo/Error.js');

var _streamingVoErrorJs2 = _interopRequireDefault(_streamingVoErrorJs);

var _streamingVoMetricsHTTPRequestJs = _dereq_('../streaming/vo/metrics/HTTPRequest.js');

var _streamingVoMetricsHTTPRequestJs2 = _interopRequireDefault(_streamingVoMetricsHTTPRequestJs);

var _coreEventsEventsJs = _dereq_('../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreEventBusJs = _dereq_('../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var _streamingUtilsURLUtilsJs = _dereq_('../streaming/utils/URLUtils.js');

var _streamingUtilsURLUtilsJs2 = _interopRequireDefault(_streamingUtilsURLUtilsJs);

var _utilsSegmentsUtilsJs = _dereq_('./utils/SegmentsUtils.js');

var _utilsSegmentsGetterJs = _dereq_('./utils/SegmentsGetter.js');

var _utilsSegmentsGetterJs2 = _interopRequireDefault(_utilsSegmentsGetterJs);

var SEGMENTS_UNAVAILABLE_ERROR_CODE = 1;

function DashHandler(config) {

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();
    var urlUtils = (0, _streamingUtilsURLUtilsJs2['default'])(context).getInstance();

    var segmentBaseLoader = config.segmentBaseLoader;
    var timelineConverter = config.timelineConverter;
    var dashMetrics = config.dashMetrics;
    var metricsModel = config.metricsModel;
    var baseURLController = config.baseURLController;

    var instance = undefined,
        index = undefined,
        requestedTime = undefined,
        isDynamic = undefined,
        type = undefined,
        currentTime = undefined,
        earliestTime = undefined,
        streamProcessor = undefined,
        segmentsGetter = undefined;

    function setup() {
        index = -1;
        currentTime = 0;
        earliestTime = NaN;
        eventBus.on(_coreEventsEventsJs2['default'].INITIALIZATION_LOADED, onInitializationLoaded, instance);
        eventBus.on(_coreEventsEventsJs2['default'].SEGMENTS_LOADED, onSegmentsLoaded, instance);
    }

    function initialize(StreamProcessor) {
        streamProcessor = StreamProcessor;
        type = streamProcessor.getType();
        isDynamic = streamProcessor.isDynamic();

        segmentsGetter = (0, _utilsSegmentsGetterJs2['default'])(context).create(config, isDynamic);
    }

    function getStreamProcessor() {
        return streamProcessor;
    }

    function setCurrentTime(value) {
        currentTime = value;
    }

    function getCurrentTime() {
        return currentTime;
    }

    function getCurrentIndex() {
        return index;
    }

    function getEarliestTime() {
        return earliestTime;
    }

    function reset() {
        segmentsGetter = null;
        currentTime = 0;
        earliestTime = NaN;
        requestedTime = NaN;
        index = -1;
        isDynamic = null;
        type = null;
        streamProcessor = null;
        eventBus.off(_coreEventsEventsJs2['default'].INITIALIZATION_LOADED, onInitializationLoaded, instance);
        eventBus.off(_coreEventsEventsJs2['default'].SEGMENTS_LOADED, onSegmentsLoaded, instance);
    }

    function unescapeDollarsInTemplate(url) {
        return url.split('$$').join('$');
    }

    function replaceIDForTemplate(url, value) {
        if (value === null || url.indexOf('$RepresentationID$') === -1) {
            return url;
        }
        var v = value.toString();
        return url.split('$RepresentationID$').join(v);
    }

    function setRequestUrl(request, destination, representation) {
        var baseURL = baseURLController.resolve(representation.path);
        var url;
        var serviceLocation;

        if (!baseURL || destination === baseURL.url || !urlUtils.isRelative(destination)) {
            url = destination;
        } else {
            url = baseURL.url + destination;
            serviceLocation = baseURL.serviceLocation;
        }

        if (urlUtils.isRelative(url)) {
            return false;
        }

        request.url = url;
        request.serviceLocation = serviceLocation;

        return true;
    }

    function generateInitRequest(representation, mediaType) {
        var request = new _streamingVoFragmentRequestJs2['default']();
        var period, presentationStartTime;

        period = representation.adaptation.period;

        request.mediaType = mediaType;
        request.type = _streamingVoMetricsHTTPRequestJs2['default'].INIT_SEGMENT_TYPE;
        request.range = representation.range;
        presentationStartTime = period.start;
        request.availabilityStartTime = timelineConverter.calcAvailabilityStartTimeFromPresentationTime(presentationStartTime, representation.adaptation.period.mpd, isDynamic);
        request.availabilityEndTime = timelineConverter.calcAvailabilityEndTimeFromPresentationTime(presentationStartTime + period.duration, period.mpd, isDynamic);
        request.quality = representation.index;
        request.mediaInfo = streamProcessor.getMediaInfo();

        if (setRequestUrl(request, representation.initialization, representation)) {
            return request;
        }
    }

    function getInitRequest(representation) {
        var request;

        if (!representation) return null;

        request = generateInitRequest(representation, type);

        //log("Got an initialization.");

        return request;
    }

    function isMediaFinished(representation) {
        var period = representation.adaptation.period;
        var segmentInfoType = representation.segmentInfoType;

        var isFinished = false;

        var sDuration, seg, fTime;

        if (index < 0) {
            isFinished = false;
        } else if (isDynamic || index < representation.availableSegmentsNumber) {
            seg = (0, _utilsSegmentsUtilsJs.getSegmentByIndex)(index, representation);

            if (seg) {
                fTime = seg.presentationStartTime - period.start;
                sDuration = representation.adaptation.period.duration;
                log(representation.segmentInfoType + ': ' + fTime + ' / ' + sDuration);
                isFinished = segmentInfoType === 'SegmentTimeline' && isDynamic ? false : fTime >= sDuration;
            }
        } else {
            isFinished = true;
        }

        return isFinished;
    }

    function updateSegments(representation) {
        return segmentsGetter.getSegments(representation, requestedTime, index, onSegmentListUpdated);
    }

    function onSegmentListUpdated(representation, segments) {

        representation.segments = segments;

        if (segments && segments.length > 0) {
            earliestTime = isNaN(earliestTime) ? segments[0].presentationStartTime : Math.min(segments[0].presentationStartTime, earliestTime);
        }

        if (isDynamic && isNaN(timelineConverter.getExpectedLiveEdge())) {
            var lastIdx = segments.length - 1;
            var lastSegment = segments[lastIdx];
            var liveEdge = lastSegment.presentationStartTime;
            var metrics = metricsModel.getMetricsFor('stream');
            // the last segment is supposed to be a live edge
            timelineConverter.setExpectedLiveEdge(liveEdge);
            metricsModel.updateManifestUpdateInfo(dashMetrics.getCurrentManifestUpdate(metrics), { presentationStartTime: liveEdge });
        }
    }

    function updateSegmentList(representation) {

        if (!representation) {
            throw new _streamingVoErrorJs2['default']('no representation');
        }

        representation.segments = null;

        updateSegments(representation);

        return representation;
    }

    function updateRepresentation(representation, keepIdx, skipLoad) {
        var hasInitialization = representation.initialization;
        var hasSegments = representation.segmentInfoType !== 'BaseURL' && representation.segmentInfoType !== 'SegmentBase';
        var error;

        if (!representation.segmentDuration && !representation.segments) {
            updateSegmentList(representation);
        }

        representation.segmentAvailabilityRange = null;
        representation.segmentAvailabilityRange = timelineConverter.calcSegmentAvailabilityRange(representation, isDynamic);

        if (representation.segmentAvailabilityRange.end < representation.segmentAvailabilityRange.start && !representation.useCalculatedLiveEdgeTime) {
            error = new _streamingVoErrorJs2['default'](SEGMENTS_UNAVAILABLE_ERROR_CODE, 'no segments are available yet', { availabilityDelay: representation.segmentAvailabilityRange.start - representation.segmentAvailabilityRange.end });
            eventBus.trigger(_coreEventsEventsJs2['default'].REPRESENTATION_UPDATED, { sender: this, representation: representation, error: error });
            return;
        }

        if (!keepIdx) index = -1;

        if (representation.segmentDuration) {
            updateSegmentList(representation);
        }

        if (!hasInitialization && !skipLoad) {
            segmentBaseLoader.loadInitialization(representation);
        }

        if (!hasSegments && !skipLoad) {
            segmentBaseLoader.loadSegments(representation, type, representation.indexRange);
        }

        if (hasInitialization && hasSegments) {
            eventBus.trigger(_coreEventsEventsJs2['default'].REPRESENTATION_UPDATED, { sender: this, representation: representation });
        }
    }

    function getIndexForSegments(time, representation, timeThreshold) {
        var segments = representation.segments;
        var ln = segments ? segments.length : null;

        var idx = -1;
        var epsilon, frag, ft, fd, i;

        if (segments && ln > 0) {
            for (i = 0; i < ln; i++) {
                frag = segments[i];
                ft = frag.presentationStartTime;
                fd = frag.duration;
                epsilon = timeThreshold === undefined || timeThreshold === null ? fd / 2 : timeThreshold;
                if (time + epsilon >= ft && time - epsilon < ft + fd) {
                    idx = frag.availabilityIdx;
                    break;
                }
            }
        }

        return idx;
    }

    function getRequestForSegment(segment) {
        if (segment === null || segment === undefined) {
            return null;
        }

        var request = new _streamingVoFragmentRequestJs2['default']();
        var representation = segment.representation;
        var bandwidth = representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].bandwidth;
        var url = segment.media;

        url = (0, _utilsSegmentsUtilsJs.replaceTokenForTemplate)(url, 'Number', segment.replacementNumber);
        url = (0, _utilsSegmentsUtilsJs.replaceTokenForTemplate)(url, 'Time', segment.replacementTime);
        url = (0, _utilsSegmentsUtilsJs.replaceTokenForTemplate)(url, 'Bandwidth', bandwidth);
        url = replaceIDForTemplate(url, representation.id);
        url = unescapeDollarsInTemplate(url);

        request.mediaType = type;
        request.type = _streamingVoMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE;
        request.range = segment.mediaRange;
        request.startTime = segment.presentationStartTime;
        request.duration = segment.duration;
        request.timescale = representation.timescale;
        request.availabilityStartTime = segment.availabilityStartTime;
        request.availabilityEndTime = segment.availabilityEndTime;
        request.wallStartTime = segment.wallStartTime;
        request.quality = representation.index;
        request.index = segment.availabilityIdx;
        request.mediaInfo = streamProcessor.getMediaInfo();
        request.adaptationIndex = representation.adaptation.index;

        if (setRequestUrl(request, url, representation)) {
            return request;
        }
    }

    function getSegmentRequestForTime(representation, time, options) {
        var request, segment, finished;

        var idx = index;

        var keepIdx = options ? options.keepIdx : false;
        var timeThreshold = options ? options.timeThreshold : null;
        var ignoreIsFinished = options && options.ignoreIsFinished ? true : false;

        if (!representation) {
            return null;
        }

        if (requestedTime !== time) {
            // When playing at live edge with 0 delay we may loop back with same time and index until it is available. Reduces verboseness of logs.
            requestedTime = time;
            log('Getting the request for ' + type + ' time : ' + time);
        }

        index = getIndexForSegments(time, representation, timeThreshold);
        //Index may be -1 if getSegments needs to update.  So after getSegments is called and updated then try to get index again.
        updateSegments(representation);
        if (index < 0) {
            index = getIndexForSegments(time, representation, timeThreshold);
        }

        if (index > 0) {
            log('Index for ' + type + ' time ' + time + ' is ' + index);
        }

        finished = !ignoreIsFinished ? isMediaFinished(representation) : false;
        if (finished) {
            request = new _streamingVoFragmentRequestJs2['default']();
            request.action = _streamingVoFragmentRequestJs2['default'].ACTION_COMPLETE;
            request.index = index;
            request.mediaType = type;
            request.mediaInfo = streamProcessor.getMediaInfo();
            log('Signal complete.', request);
        } else {
            segment = (0, _utilsSegmentsUtilsJs.getSegmentByIndex)(index, representation);
            request = getRequestForSegment(segment);
        }

        if (keepIdx && idx >= 0) {
            index = representation.segmentInfoType === 'SegmentTimeline' && isDynamic ? index : idx;
        }

        return request;
    }

    function generateSegmentRequestForTime(representation, time) {
        var step = (representation.segmentAvailabilityRange.end - representation.segmentAvailabilityRange.start) / 2;

        representation.segments = null;
        representation.segmentAvailabilityRange = { start: time - step, end: time + step };
        return getSegmentRequestForTime(representation, time, { keepIdx: false, ignoreIsFinished: true });
    }

    function getNextSegmentRequest(representation) {
        var request, segment, finished;

        if (!representation || index === -1) {
            return null;
        }

        requestedTime = null;
        index++;

        log('Getting the next request at index: ' + index);

        finished = isMediaFinished(representation);
        if (finished) {
            request = new _streamingVoFragmentRequestJs2['default']();
            request.action = _streamingVoFragmentRequestJs2['default'].ACTION_COMPLETE;
            request.index = index;
            request.mediaType = type;
            request.mediaInfo = streamProcessor.getMediaInfo();
            log('Signal complete.');
        } else {
            updateSegments(representation);
            segment = (0, _utilsSegmentsUtilsJs.getSegmentByIndex)(index, representation);
            request = getRequestForSegment(segment);
            if (!segment && isDynamic) {
                /*
                 Sometimes when playing dynamic streams with 0 fragment delay at live edge we ask for
                 an index before it is available so we decrement index back and send null request
                 which triggers the validate loop to rerun and the next time the segment should be
                 available.
                 */
                index--;
            }
        }

        return request;
    }

    function onInitializationLoaded(e) {
        var representation = e.representation;
        //log("Got an initialization.");
        if (!representation.segments) return;

        eventBus.trigger(_coreEventsEventsJs2['default'].REPRESENTATION_UPDATED, { sender: this, representation: representation });
    }

    function onSegmentsLoaded(e) {
        if (e.error || type !== e.mediaType) return;

        var fragments = e.segments;
        var representation = e.representation;
        var segments = [];
        var count = 0;

        var i, len, s, seg;

        for (i = 0, len = fragments.length; i < len; i++) {
            s = fragments[i];

            seg = (0, _utilsSegmentsUtilsJs.getTimeBasedSegment)(timelineConverter, isDynamic, representation, s.startTime, s.duration, s.timescale, s.media, s.mediaRange, count);

            segments.push(seg);
            seg = null;
            count++;
        }

        representation.segmentAvailabilityRange = { start: segments[0].presentationStartTime, end: segments[len - 1].presentationStartTime };
        representation.availableSegmentsNumber = len;

        onSegmentListUpdated(representation, segments);

        if (!representation.initialization) return;

        eventBus.trigger(_coreEventsEventsJs2['default'].REPRESENTATION_UPDATED, { sender: this, representation: representation });
    }

    instance = {
        initialize: initialize,
        getStreamProcessor: getStreamProcessor,
        getInitRequest: getInitRequest,
        getSegmentRequestForTime: getSegmentRequestForTime,
        getNextSegmentRequest: getNextSegmentRequest,
        generateSegmentRequestForTime: generateSegmentRequestForTime,
        updateRepresentation: updateRepresentation,
        setCurrentTime: setCurrentTime,
        getCurrentTime: getCurrentTime,
        getCurrentIndex: getCurrentIndex,
        getEarliestTime: getEarliestTime,
        reset: reset
    };

    setup();

    return instance;
}

DashHandler.__dashjs_factory_name = 'DashHandler';
var factory = _coreFactoryMakerJs2['default'].getClassFactory(DashHandler);
factory.SEGMENTS_UNAVAILABLE_ERROR_CODE = SEGMENTS_UNAVAILABLE_ERROR_CODE;
exports['default'] = factory;
module.exports = exports['default'];

},{"../core/Debug.js":5,"../core/EventBus.js":6,"../core/FactoryMaker.js":7,"../core/events/Events.js":9,"../streaming/utils/URLUtils.js":96,"../streaming/vo/Error.js":100,"../streaming/vo/FragmentRequest.js":101,"../streaming/vo/metrics/HTTPRequest.js":117,"./utils/SegmentsGetter.js":20,"./utils/SegmentsUtils.js":21}],13:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _streamingVoMetricsHTTPRequestJs = _dereq_('../streaming/vo/metrics/HTTPRequest.js');

var _streamingVoMetricsHTTPRequestJs2 = _interopRequireDefault(_streamingVoMetricsHTTPRequestJs);

var _streamingControllersAbrControllerJs = _dereq_('../streaming/controllers/AbrController.js');

var _streamingControllersAbrControllerJs2 = _interopRequireDefault(_streamingControllersAbrControllerJs);

var _streamingModelsManifestModelJs = _dereq_('../streaming/models/ManifestModel.js');

var _streamingModelsManifestModelJs2 = _interopRequireDefault(_streamingModelsManifestModelJs);

var _modelsDashManifestModelJs = _dereq_('./models/DashManifestModel.js');

var _modelsDashManifestModelJs2 = _interopRequireDefault(_modelsDashManifestModelJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

/**
 * @Module DashMetrics
 */
function DashMetrics() {

    var instance = undefined;
    var context = this.context;
    var manifestModel = (0, _streamingModelsManifestModelJs2['default'])(context).getInstance(); //TODO Need to pass this in not bake in

    function getBandwidthForRepresentation(representationId, periodId) {
        var representation;
        var manifest = manifestModel.getValue();
        var period = manifest.Period_asArray[periodId];

        representation = findRepresentation(period, representationId);

        if (representation === null) {
            return null;
        }

        return representation.bandwidth;
    }

    /**
     *
     * @param representationId
     * @param periodIdx
     * @returns {*}
     */
    function getIndexForRepresentation(representationId, periodIdx) {
        var representationIndex;
        var manifest = manifestModel.getValue();
        var period = manifest.Period_asArray[periodIdx];

        representationIndex = findRepresentationIndex(period, representationId);
        return representationIndex;
    }

    /**
     * This method returns the current max index based on what is defined in the MPD.
     *
     * @param bufferType - String 'audio' or 'video',
     * @param periodIdx - Make sure this is the period index not id
     * @return int
     * @memberof module:DashMetrics
     * @instance
     */
    function getMaxIndexForBufferType(bufferType, periodIdx) {
        var maxIndex;
        var manifest = manifestModel.getValue();
        var period = manifest.Period_asArray[periodIdx];

        maxIndex = findMaxBufferIndex(period, bufferType);
        return maxIndex;
    }

    /**
     * This method returns the current max index correlated to the max allowed bitrate
     * explicitly set via the MediaPlayer's API setMaxAllowedBitrateFor.
     *
     * @param bufferType - String 'audio' or 'video',
     * @param periodId - Make sure this is the period id not index.
     * @return int
     * @see {@link module:MediaPlayer#setMaxAllowedBitrateFor setMaxAllowedBitrateFor()}
     * @see {@link DashMetrics#getMaxIndexForBufferType getMaxIndexForBufferType()}
     * @memberof module:DashMetrics
     * @instance
     */
    function getMaxAllowedIndexForBufferType(bufferType, periodId) {
        var idx = 0;
        var abrController = (0, _streamingControllersAbrControllerJs2['default'])(context).getInstance();

        if (abrController) {
            idx = abrController.getTopQualityIndexFor(bufferType, periodId);
        }

        return idx;
    }

    /**
     * @param metrics
     * @returns {*}
     * @memberof module:DashMetrics
     * @instance
     */
    function getCurrentRepresentationSwitch(metrics) {
        if (metrics === null) {
            return null;
        }

        var repSwitch = metrics.RepSwitchList;
        var repSwitchLength, repSwitchLastIndex, currentRepSwitch;

        if (repSwitch === null || repSwitch.length <= 0) {
            return null;
        }

        repSwitchLength = repSwitch.length;
        repSwitchLastIndex = repSwitchLength - 1;

        currentRepSwitch = repSwitch[repSwitchLastIndex];
        return currentRepSwitch;
    }

    /**
     * @param metrics
     * @returns {*}
     * @memberof module:DashMetrics
     * @instance
     */
    function getLatestBufferLevelVO(metrics) {
        if (metrics === null) {
            return null;
        }

        var bufferLevel = metrics.BufferLevel;
        if (bufferLevel === null || bufferLevel.length <= 0) {
            return null;
        }

        return bufferLevel[bufferLevel.length - 1];
    }

    /**
     * @param metrics
     * @returns {number}
     * @memberof module:DashMetrics
     * @instance
     */
    function getCurrentBufferLevel(metrics) {
        if (metrics === null) {
            return 0;
        }

        var bufferLevel = metrics.BufferLevel;
        if (bufferLevel === null || bufferLevel.length <= 0) {
            return 0;
        }

        return bufferLevel[bufferLevel.length - 1].level / 1000;
    }

    /**
     * @param metrics
     * @returns {null|*|vo}
     * @memberof module:DashMetrics
     * @instance
     */
    function getRequestsQueue(metrics) {
        return metrics.RequestsQueue;
    }

    /**
     * @param metrics
     * @returns {*}
     * @memberof module:DashMetrics
     * @instance
     */
    function getCurrentHttpRequest(metrics) {
        if (metrics === null) {
            return null;
        }

        var httpList = metrics.HttpList;
        var currentHttpList = null;

        var httpListLength, httpListLastIndex;

        if (httpList === null || httpList.length <= 0) {
            return null;
        }

        httpListLength = httpList.length;
        httpListLastIndex = httpListLength - 1;

        while (httpListLastIndex >= 0) {
            if (httpList[httpListLastIndex].responsecode) {
                currentHttpList = httpList[httpListLastIndex];
                break;
            }
            httpListLastIndex--;
        }
        return currentHttpList;
    }

    /**
     * @param metrics
     * @returns {*}
     * @memberof module:DashMetrics
     * @instance
     */
    function getHttpRequests(metrics) {
        if (metrics === null) {
            return [];
        }

        return !!metrics.HttpList ? metrics.HttpList : [];
    }

    /**
     * @param metrics
     * @returns {*}
     * @memberof module:DashMetrics
     * @instance
     */
    function getCurrentDroppedFrames(metrics) {
        if (metrics === null) {
            return null;
        }

        var droppedFrames = metrics.DroppedFrames;
        var droppedFramesLength, droppedFramesLastIndex, currentDroppedFrames;

        if (droppedFrames === null || droppedFrames.length <= 0) {
            return null;
        }

        droppedFramesLength = droppedFrames.length;
        droppedFramesLastIndex = droppedFramesLength - 1;
        currentDroppedFrames = droppedFrames[droppedFramesLastIndex];

        return currentDroppedFrames;
    }

    /**
     * @param metrics
     * @returns {*}
     * @memberof module:DashMetrics
     * @instance
     */
    function getCurrentSchedulingInfo(metrics) {
        if (metrics === null) return null;

        var schedulingInfo = metrics.SchedulingInfo;
        var ln, lastIdx, currentSchedulingInfo;

        if (schedulingInfo === null || schedulingInfo.length <= 0) {
            return null;
        }

        ln = schedulingInfo.length;
        lastIdx = ln - 1;

        currentSchedulingInfo = schedulingInfo[lastIdx];

        return currentSchedulingInfo;
    }

    /**
     * @param metrics
     * @returns {*}
     * @memberof module:DashMetrics
     * @instance
     */
    function getCurrentManifestUpdate(metrics) {
        if (metrics === null) return null;

        var manifestUpdate = metrics.ManifestUpdate;
        var ln, lastIdx, currentManifestUpdate;

        if (manifestUpdate === null || manifestUpdate.length <= 0) {
            return null;
        }

        ln = manifestUpdate.length;
        lastIdx = ln - 1;

        currentManifestUpdate = manifestUpdate[lastIdx];

        return currentManifestUpdate;
    }

    /**
     * @param metrics
     * @returns {*}
     * @memberof module:DashMetrics
     * @instance
     */
    function getCurrentDVRInfo(metrics) {

        if (metrics === null) {
            return null;
        }

        var dvrInfo = metrics.DVRInfo;
        var dvrInfoLastIndex, currentDVRInfo;

        if (dvrInfo === null || dvrInfo.length <= 0) {
            return null;
        }

        dvrInfoLastIndex = dvrInfo.length - 1;
        currentDVRInfo = dvrInfo[dvrInfoLastIndex];

        return currentDVRInfo;
    }

    /**
     * @param metrics
     * @param id
     * @returns {*}
     * @memberof module:DashMetrics
     * @instance
     */
    function getLatestMPDRequestHeaderValueByID(metrics, id) {
        var headers = {};
        var httpRequestList, httpRequest, i;

        if (metrics === null) {
            return null;
        }

        httpRequestList = getHttpRequests(metrics);

        for (i = httpRequestList.length - 1; i >= 0; i--) {
            httpRequest = httpRequestList[i];

            if (httpRequest.type === _streamingVoMetricsHTTPRequestJs2['default'].MPD_TYPE) {
                headers = parseResponseHeaders(httpRequest._responseHeaders);
                break;
            }
        }

        return headers[id] === undefined ? null : headers[id];
    }

    /**
     * @param metrics
     * @param id
     * @returns {*}
     * @memberof module:DashMetrics
     * @instance
     */
    function getLatestFragmentRequestHeaderValueByID(metrics, id) {

        if (metrics === null) return null;

        var httpRequest = getCurrentHttpRequest(metrics);
        var headers;

        if (httpRequest === null || httpRequest._responseHeaders === null) return null;

        headers = parseResponseHeaders(httpRequest._responseHeaders);
        return headers[id] === undefined ? null : headers[id];
    }

    function parseResponseHeaders(headerStr) {
        var headers = {};
        if (!headerStr) {
            return headers;
        }
        var headerPairs = headerStr.split('\r\n');
        for (var i = 0, ilen = headerPairs.length; i < ilen; i++) {
            var headerPair = headerPairs[i];
            var index = headerPair.indexOf(': ');
            if (index > 0) {
                headers[headerPair.substring(0, index)] = headerPair.substring(index + 2);
            }
        }
        return headers;
    }

    function findRepresentationIndex(period, representationId) {
        var adaptationSet, adaptationSetArray, representation, representationArray, adaptationSetArrayIndex, representationArrayIndex;

        adaptationSetArray = period.AdaptationSet_asArray;
        for (adaptationSetArrayIndex = 0; adaptationSetArrayIndex < adaptationSetArray.length; adaptationSetArrayIndex = adaptationSetArrayIndex + 1) {
            adaptationSet = adaptationSetArray[adaptationSetArrayIndex];
            representationArray = adaptationSet.Representation_asArray;
            for (representationArrayIndex = 0; representationArrayIndex < representationArray.length; representationArrayIndex = representationArrayIndex + 1) {
                representation = representationArray[representationArrayIndex];
                if (representationId === representation.id) {
                    return representationArrayIndex;
                }
            }
        }

        return -1;
    }

    function findRepresentation(period, representationId) {
        var adaptationSet, adaptationSetArray, representation, representationArray, adaptationSetArrayIndex, representationArrayIndex;

        adaptationSetArray = period.AdaptationSet_asArray;
        for (adaptationSetArrayIndex = 0; adaptationSetArrayIndex < adaptationSetArray.length; adaptationSetArrayIndex = adaptationSetArrayIndex + 1) {
            adaptationSet = adaptationSetArray[adaptationSetArrayIndex];
            representationArray = adaptationSet.Representation_asArray;
            for (representationArrayIndex = 0; representationArrayIndex < representationArray.length; representationArrayIndex = representationArrayIndex + 1) {
                representation = representationArray[representationArrayIndex];
                if (representationId === representation.id) {
                    return representation;
                }
            }
        }

        return null;
    }

    function adaptationIsType(adaptation, bufferType) {
        return (0, _modelsDashManifestModelJs2['default'])(context).getInstance().getIsTypeOf(adaptation, bufferType);
    }

    function findMaxBufferIndex(period, bufferType) {
        var adaptationSet, adaptationSetArray, representationArray, adaptationSetArrayIndex;

        if (!period || !bufferType) return -1;

        adaptationSetArray = period.AdaptationSet_asArray;
        for (adaptationSetArrayIndex = 0; adaptationSetArrayIndex < adaptationSetArray.length; adaptationSetArrayIndex = adaptationSetArrayIndex + 1) {
            adaptationSet = adaptationSetArray[adaptationSetArrayIndex];
            representationArray = adaptationSet.Representation_asArray;
            if (adaptationIsType(adaptationSet, bufferType)) {
                return representationArray.length;
            }
        }

        return -1;
    }

    instance = {
        getBandwidthForRepresentation: getBandwidthForRepresentation,
        getIndexForRepresentation: getIndexForRepresentation,
        getMaxIndexForBufferType: getMaxIndexForBufferType,
        getMaxAllowedIndexForBufferType: getMaxAllowedIndexForBufferType,
        getCurrentRepresentationSwitch: getCurrentRepresentationSwitch,
        getLatestBufferLevelVO: getLatestBufferLevelVO,
        getCurrentBufferLevel: getCurrentBufferLevel,
        getCurrentHttpRequest: getCurrentHttpRequest,
        getHttpRequests: getHttpRequests,
        getCurrentDroppedFrames: getCurrentDroppedFrames,
        getCurrentSchedulingInfo: getCurrentSchedulingInfo,
        getCurrentDVRInfo: getCurrentDVRInfo,
        getCurrentManifestUpdate: getCurrentManifestUpdate,
        getLatestFragmentRequestHeaderValueByID: getLatestFragmentRequestHeaderValueByID,
        getLatestMPDRequestHeaderValueByID: getLatestMPDRequestHeaderValueByID,
        getRequestsQueue: getRequestsQueue
    };

    return instance;
}

DashMetrics.__dashjs_factory_name = 'DashMetrics';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(DashMetrics);
module.exports = exports['default'];

},{"../core/FactoryMaker.js":7,"../streaming/controllers/AbrController.js":46,"../streaming/models/ManifestModel.js":63,"../streaming/vo/metrics/HTTPRequest.js":117,"./models/DashManifestModel.js":17}],14:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _streamingUtilsErrorHandlerJs = _dereq_('../streaming/utils/ErrorHandler.js');

var _streamingUtilsErrorHandlerJs2 = _interopRequireDefault(_streamingUtilsErrorHandlerJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var _externalsObjectironJs = _dereq_('../../externals/objectiron.js');

var _externalsObjectironJs2 = _interopRequireDefault(_externalsObjectironJs);

var _externalsXml2jsonJs = _dereq_('../../externals/xml2json.js');

var _externalsXml2jsonJs2 = _interopRequireDefault(_externalsXml2jsonJs);

function DashParser() /*config*/{

    var SECONDS_IN_YEAR = 365 * 24 * 60 * 60;
    var SECONDS_IN_MONTH = 30 * 24 * 60 * 60;
    var SECONDS_IN_DAY = 24 * 60 * 60;
    var SECONDS_IN_HOUR = 60 * 60;
    var SECONDS_IN_MIN = 60;
    var MINUTES_IN_HOUR = 60;
    var MILLISECONDS_IN_SECONDS = 1000;

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;

    var instance = undefined,
        durationRegex = undefined,
        datetimeRegex = undefined,
        numericRegex = undefined,
        httpOrHttpsRegex = undefined,
        originRegex = undefined,
        matchers = undefined;

    function setup() {

        durationRegex = /^([-])?P(([\d.]*)Y)?(([\d.]*)M)?(([\d.]*)D)?T?(([\d.]*)H)?(([\d.]*)M)?(([\d.]*)S)?/;
        datetimeRegex = /^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2})(?::([0-9]*)(\.[0-9]*)?)?(?:([+-])([0-9]{2})([0-9]{2}))?/;
        numericRegex = /^[-+]?[0-9]+[.]?[0-9]*([eE][-+]?[0-9]+)?$/;
        httpOrHttpsRegex = /^https?:\/\//i;
        originRegex = /^(https?:\/\/[^\/]+)\/?/i;
        //TODO-Refactor clean this up and move into its own util or somewhere else.
        matchers = [{
            type: 'duration',
            test: function test(attr) {

                var attributeList = ['minBufferTime', 'mediaPresentationDuration', 'minimumUpdatePeriod', 'timeShiftBufferDepth', 'maxSegmentDuration', 'maxSubsegmentDuration', 'suggestedPresentationDelay', 'start', 'starttime', 'duration'];
                var len = attributeList.length;

                for (var i = 0; i < len; i++) {
                    if (attr.nodeName === attributeList[i]) {
                        return durationRegex.test(attr.value);
                    }
                }
                return false;
            },
            converter: function converter(str) {
                //str = "P10Y10M10DT10H10M10.1S";
                var match = durationRegex.exec(str);
                var result = parseFloat(match[2] || 0) * SECONDS_IN_YEAR + parseFloat(match[4] || 0) * SECONDS_IN_MONTH + parseFloat(match[6] || 0) * SECONDS_IN_DAY + parseFloat(match[8] || 0) * SECONDS_IN_HOUR + parseFloat(match[10] || 0) * SECONDS_IN_MIN + parseFloat(match[12] || 0);

                if (match[1] !== undefined) {
                    result = -result;
                }

                return result;
            }
        }, {
            type: 'datetime',
            test: function test(attr) {
                return datetimeRegex.test(attr.value);
            },
            converter: function converter(str) {
                var match = datetimeRegex.exec(str);
                var utcDate;
                // If the string does not contain a timezone offset different browsers can interpret it either
                // as UTC or as a local time so we have to parse the string manually to normalize the given date value for
                // all browsers
                utcDate = Date.UTC(parseInt(match[1], 10), parseInt(match[2], 10) - 1, // months start from zero
                parseInt(match[3], 10), parseInt(match[4], 10), parseInt(match[5], 10), match[6] && parseInt(match[6], 10) || 0, match[7] && parseFloat(match[7]) * MILLISECONDS_IN_SECONDS || 0);
                // If the date has timezone offset take it into account as well
                if (match[9] && match[10]) {
                    var timezoneOffset = parseInt(match[9], 10) * MINUTES_IN_HOUR + parseInt(match[10], 10);
                    utcDate += (match[8] === '+' ? -1 : +1) * timezoneOffset * SECONDS_IN_MIN * MILLISECONDS_IN_SECONDS;
                }

                return new Date(utcDate);
            }
        }, {
            type: 'numeric',
            test: function test(attr) {
                return numericRegex.test(attr.value);
            },
            converter: function converter(str) {
                return parseFloat(str);
            }
        }];
    }

    function getCommonValuesMap() {
        var adaptationSet, representation, subRepresentation, common;

        common = [{
            name: 'profiles',
            merge: false
        }, {
            name: 'width',
            merge: false
        }, {
            name: 'height',
            merge: false
        }, {
            name: 'sar',
            merge: false
        }, {
            name: 'frameRate',
            merge: false
        }, {
            name: 'audioSamplingRate',
            merge: false
        }, {
            name: 'mimeType',
            merge: false
        }, {
            name: 'segmentProfiles',
            merge: false
        }, {
            name: 'codecs',
            merge: false
        }, {
            name: 'maximumSAPPeriod',
            merge: false
        }, {
            name: 'startsWithSap',
            merge: false
        }, {
            name: 'maxPlayoutRate',
            merge: false
        }, {
            name: 'codingDependency',
            merge: false
        }, {
            name: 'scanType',
            merge: false
        }, {
            name: 'FramePacking',
            merge: true
        }, {
            name: 'AudioChannelConfiguration',
            merge: true
        }, {
            name: 'ContentProtection',
            merge: true
        }];

        adaptationSet = {};
        adaptationSet.name = 'AdaptationSet';
        adaptationSet.isRoot = false;
        adaptationSet.isArray = true;
        adaptationSet.parent = null;
        adaptationSet.children = [];
        adaptationSet.properties = common;

        representation = {};
        representation.name = 'Representation';
        representation.isRoot = false;
        representation.isArray = true;
        representation.parent = adaptationSet;
        representation.children = [];
        representation.properties = common;
        adaptationSet.children.push(representation);

        subRepresentation = {};
        subRepresentation.name = 'SubRepresentation';
        subRepresentation.isRoot = false;
        subRepresentation.isArray = true;
        subRepresentation.parent = representation;
        subRepresentation.children = [];
        subRepresentation.properties = common;
        representation.children.push(subRepresentation);

        return adaptationSet;
    }

    function getSegmentValuesMap() {
        var period, adaptationSet, representation, common;

        common = [{
            name: 'SegmentBase',
            merge: true
        }, {
            name: 'SegmentTemplate',
            merge: true
        }, {
            name: 'SegmentList',
            merge: true
        }];

        period = {};
        period.name = 'Period';
        period.isRoot = false;
        period.isArray = true;
        period.parent = null;
        period.children = [];
        period.properties = common;

        adaptationSet = {};
        adaptationSet.name = 'AdaptationSet';
        adaptationSet.isRoot = false;
        adaptationSet.isArray = true;
        adaptationSet.parent = period;
        adaptationSet.children = [];
        adaptationSet.properties = common;
        period.children.push(adaptationSet);

        representation = {};
        representation.name = 'Representation';
        representation.isRoot = false;
        representation.isArray = true;
        representation.parent = adaptationSet;
        representation.children = [];
        representation.properties = common;
        adaptationSet.children.push(representation);

        return period;
    }

    function getDashMap() {
        var result = [];

        result.push(getCommonValuesMap());
        result.push(getSegmentValuesMap());

        return result;
    }

    function parse(data, xlinkController) {

        var converter = new _externalsXml2jsonJs2['default'](matchers, '', true);
        var iron = new _externalsObjectironJs2['default'](getDashMap());
        var start = new Date();

        var manifest;
        var json = null;
        var ironed = null;

        try {
            //log("Converting from XML.");
            manifest = converter.xml_str2json(data);

            if (!manifest) {
                throw 'parser error';
            }

            json = new Date();

            if (manifest.hasOwnProperty('Location')) {
                // for now, do not support multiple Locations -
                // just set Location to the first Location.
                manifest.Location = manifest.Location_asArray[0];
            }

            //log("Flatten manifest properties.");
            iron.run(manifest);
            ironed = new Date();

            xlinkController.setMatchers(matchers);
            xlinkController.setIron(iron);

            log('Parsing complete: ( xml2json: ' + (json.getTime() - start.getTime()) + 'ms, objectiron: ' + (ironed.getTime() - json.getTime()) + 'ms, total: ' + (ironed.getTime() - start.getTime()) / 1000 + 's)');
        } catch (err) {
            (0, _streamingUtilsErrorHandlerJs2['default'])(context).getInstance().manifestError('parsing the manifest failed', 'parse', data);
            return null;
        }
        return manifest;
    }

    instance = {
        parse: parse
    };

    setup();
    return instance;
}

DashParser.__dashjs_factory_name = 'DashParser';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(DashParser);
module.exports = exports['default'];

},{"../../externals/objectiron.js":2,"../../externals/xml2json.js":3,"../core/Debug.js":5,"../core/FactoryMaker.js":7,"../streaming/utils/ErrorHandler.js":90}],15:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _streamingUtilsRequestModifierJs = _dereq_('../streaming/utils/RequestModifier.js');

var _streamingUtilsRequestModifierJs2 = _interopRequireDefault(_streamingUtilsRequestModifierJs);

var _voSegmentJs = _dereq_('./vo/Segment.js');

var _voSegmentJs2 = _interopRequireDefault(_voSegmentJs);

var _streamingVoErrorJs = _dereq_('../streaming/vo/Error.js');

var _streamingVoErrorJs2 = _interopRequireDefault(_streamingVoErrorJs);

var _streamingUtilsErrorHandlerJs = _dereq_('../streaming/utils/ErrorHandler.js');

var _streamingUtilsErrorHandlerJs2 = _interopRequireDefault(_streamingUtilsErrorHandlerJs);

var _coreEventsEventsJs = _dereq_('../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreEventBusJs = _dereq_('../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _streamingUtilsBoxParserJs = _dereq_('../streaming/utils/BoxParser.js');

var _streamingUtilsBoxParserJs2 = _interopRequireDefault(_streamingUtilsBoxParserJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

function SegmentBaseLoader() {

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        errHandler = undefined,
        boxParser = undefined,
        requestModifier = undefined,
        baseURLController = undefined;

    function initialize() {
        errHandler = (0, _streamingUtilsErrorHandlerJs2['default'])(context).getInstance();
        boxParser = (0, _streamingUtilsBoxParserJs2['default'])(context).getInstance();
        requestModifier = (0, _streamingUtilsRequestModifierJs2['default'])(context).getInstance();
    }

    function setConfig(config) {
        if (config.baseURLController) {
            baseURLController = config.baseURLController;
        }
    }

    function loadInitialization(representation, loadingInfo) {
        var needFailureReport = true;
        var initRange = null;
        var isoFile = null;
        var request = window.hola_cdn && window.hola_cdn.api && window.hola_cdn.api.new_http_request && window.hola_cdn.api.new_http_request({}) || new XMLHttpRequest();
        var baseUrl = baseURLController.resolve(representation.path);
        var info = loadingInfo || {
            url: baseUrl ? baseUrl.url : undefined,
            range: {
                start: 0,
                end: 1500
            },
            searching: false,
            bytesLoaded: 0,
            bytesToLoad: 1500,
            request: request
        };

        log('Start searching for initialization.');

        request.onload = function () {
            if (request.status < 200 || request.status > 299) return;

            needFailureReport = false;

            info.bytesLoaded = info.range.end;
            isoFile = boxParser.parse(request.response);
            initRange = findInitRange(isoFile);

            if (initRange) {
                representation.range = initRange;
                representation.initialization = info.url;
                eventBus.trigger(_coreEventsEventsJs2['default'].INITIALIZATION_LOADED, { representation: representation });
            } else {
                info.range.end = info.bytesLoaded + info.bytesToLoad;
                loadInitialization(representation, info);
            }
        };

        request.onloadend = request.onerror = function () {
            if (!needFailureReport) return;
            needFailureReport = false;

            errHandler.downloadError('initialization', info.url, request);
            eventBus.trigger(_coreEventsEventsJs2['default'].INITIALIZATION_LOADED, { representation: representation });
        };

        sendRequest(request, info);
        log('Perform init search: ' + info.url);
    }

    function loadSegments(representation, type, range, loadingInfo, callback) {
        if (range && (range.start === undefined || range.end === undefined)) {
            var parts = range ? range.toString().split('-') : null;
            range = parts ? { start: parseFloat(parts[0]), end: parseFloat(parts[1]) } : null;
        }

        callback = !callback ? onLoaded : callback;
        var needFailureReport = true;
        var isoFile = null;
        var sidx = null;
        var hasRange = !!range;
        var request = window.hola_cdn && window.hola_cdn.api && window.hola_cdn.api.new_http_request && window.hola_cdn.api.new_http_request({}) || new XMLHttpRequest();
        var baseUrl = baseURLController.resolve(representation.path);
        var info = {
            url: baseUrl ? baseUrl.url : undefined,
            range: hasRange ? range : { start: 0, end: 1500 },
            searching: !hasRange,
            bytesLoaded: loadingInfo ? loadingInfo.bytesLoaded : 0,
            bytesToLoad: 1500,
            request: request
        };

        request.onload = function () {
            if (request.status < 200 || request.status > 299) return;

            var extraBytes = info.bytesToLoad;
            var loadedLength = request.response.byteLength;

            needFailureReport = false;
            info.bytesLoaded = info.range.end - info.range.start;
            isoFile = boxParser.parse(request.response);
            sidx = isoFile.getBox('sidx');

            if (!sidx || !sidx.isComplete) {
                if (sidx) {
                    info.range.start = sidx.offset || info.range.start;
                    info.range.end = info.range.start + (sidx.size || extraBytes);
                } else if (loadedLength < info.bytesLoaded) {
                    // if we have reached a search limit or if we have reached the end of the file we have to stop trying to find sidx
                    callback(null, representation, type);
                    return;
                } else {
                    var lastBox = isoFile.getLastBox();

                    if (lastBox && lastBox.size) {
                        info.range.start = lastBox.offset + lastBox.size;
                        info.range.end = info.range.start + extraBytes;
                    } else {
                        info.range.end += extraBytes;
                    }
                }
                loadSegments(representation, type, info.range, info, callback);
            } else {
                var ref = sidx.references;
                var loadMultiSidx, segments;

                if (ref !== null && ref !== undefined && ref.length > 0) {
                    loadMultiSidx = ref[0].reference_type === 1;
                }

                if (loadMultiSidx) {
                    log('Initiate multiple SIDX load.');
                    info.range.end = info.range.start + sidx.size;

                    var j, len, ss, se, r;
                    var segs = [];
                    var count = 0;
                    var offset = (sidx.offset || info.range.start) + sidx.size;
                    var tmpCallback = function tmpCallback(result) {
                        if (result) {
                            segs = segs.concat(result);
                            count++;

                            if (count >= len) {
                                callback(segs, representation, type);
                            }
                        } else {
                            callback(null, representation, type);
                        }
                    };

                    for (j = 0, len = ref.length; j < len; j++) {
                        ss = offset;
                        se = offset + ref[j].referenced_size - 1;
                        offset = offset + ref[j].referenced_size;
                        r = { start: ss, end: se };
                        loadSegments(representation, null, r, info, tmpCallback);
                    }
                } else {
                    log('Parsing segments from SIDX.');
                    segments = getSegmentsForSidx(sidx, info);
                    callback(segments, representation, type);
                }
            }
        };

        request.onloadend = request.onerror = function () {
            if (!needFailureReport) return;

            needFailureReport = false;
            errHandler.downloadError('SIDX', info.url, request);
            callback(null, representation, type);
        };

        sendRequest(request, info);
        log('Perform SIDX load: ' + info.url);
    }

    function reset() {
        errHandler = null;
        boxParser = null;
        requestModifier = null;
        log = null;
    }

    function getSegmentsForSidx(sidx, info) {

        var refs = sidx.references;
        var len = refs.length;
        var timescale = sidx.timescale;
        var time = sidx.earliest_presentation_time;
        var start = info.range.start + sidx.first_offset + sidx.size;
        var segments = [];
        var segment, end, duration, size;

        for (var i = 0; i < len; i++) {
            duration = refs[i].subsegment_duration;
            size = refs[i].referenced_size;

            segment = new _voSegmentJs2['default']();
            segment.duration = duration;
            segment.media = info.url;
            segment.startTime = time;
            segment.timescale = timescale;
            end = start + size - 1;
            segment.mediaRange = start + '-' + end;
            segments.push(segment);
            time += duration;
            start += size;
        }

        return segments;
    }

    function findInitRange(isoFile) {
        var ftyp = isoFile.getBox('ftyp');
        var moov = isoFile.getBox('moov');

        var initRange = null;
        var start, end;

        log('Searching for initialization.');

        if (moov && moov.isComplete) {
            start = ftyp ? ftyp.offset : moov.offset;
            end = moov.offset + moov.size - 1;
            initRange = start + '-' + end;

            log('Found the initialization.  Range: ' + initRange);
        }

        return initRange;
    }

    function sendRequest(request, info) {
        if (!info.url) {
            return;
        }

        request.open('GET', requestModifier.modifyRequestURL(info.url));
        request.responseType = 'arraybuffer';
        request.setRequestHeader('Range', 'bytes=' + info.range.start + '-' + info.range.end);
        request = requestModifier.modifyRequestHeader(request);
        request.send(null);
    }

    function onLoaded(segments, representation, type) {
        if (segments) {
            eventBus.trigger(_coreEventsEventsJs2['default'].SEGMENTS_LOADED, { segments: segments, representation: representation, mediaType: type });
        } else {
            eventBus.trigger(_coreEventsEventsJs2['default'].SEGMENTS_LOADED, { segments: null, representation: representation, mediaType: type, error: new _streamingVoErrorJs2['default'](null, 'error loading segments', null) });
        }
    }

    instance = {
        setConfig: setConfig,
        initialize: initialize,
        loadInitialization: loadInitialization,
        loadSegments: loadSegments,
        reset: reset
    };

    return instance;
}

SegmentBaseLoader.__dashjs_factory_name = 'SegmentBaseLoader';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(SegmentBaseLoader);
module.exports = exports['default'];

},{"../core/Debug.js":5,"../core/EventBus.js":6,"../core/FactoryMaker.js":7,"../core/events/Events.js":9,"../streaming/utils/BoxParser.js":86,"../streaming/utils/ErrorHandler.js":90,"../streaming/utils/RequestModifier.js":94,"../streaming/vo/Error.js":100,"./vo/Segment.js":32}],16:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _modelsDashManifestModelJs = _dereq_('../models/DashManifestModel.js');

var _modelsDashManifestModelJs2 = _interopRequireDefault(_modelsDashManifestModelJs);

var _DashMetricsJs = _dereq_('../DashMetrics.js');

var _DashMetricsJs2 = _interopRequireDefault(_DashMetricsJs);

var _utilsTimelineConverterJs = _dereq_('../utils/TimelineConverter.js');

var _utilsTimelineConverterJs2 = _interopRequireDefault(_utilsTimelineConverterJs);

var _streamingControllersAbrControllerJs = _dereq_('../../streaming/controllers/AbrController.js');

var _streamingControllersAbrControllerJs2 = _interopRequireDefault(_streamingControllersAbrControllerJs);

var _streamingControllersPlaybackControllerJs = _dereq_('../../streaming/controllers/PlaybackController.js');

var _streamingControllersPlaybackControllerJs2 = _interopRequireDefault(_streamingControllersPlaybackControllerJs);

var _streamingControllersStreamControllerJs = _dereq_('../../streaming/controllers/StreamController.js');

var _streamingControllersStreamControllerJs2 = _interopRequireDefault(_streamingControllersStreamControllerJs);

var _streamingModelsManifestModelJs = _dereq_('../../streaming/models/ManifestModel.js');

var _streamingModelsManifestModelJs2 = _interopRequireDefault(_streamingModelsManifestModelJs);

var _streamingModelsMetricsModelJs = _dereq_('../../streaming/models/MetricsModel.js');

var _streamingModelsMetricsModelJs2 = _interopRequireDefault(_streamingModelsMetricsModelJs);

var _streamingModelsMediaPlayerModelJs = _dereq_('../../streaming/models/MediaPlayerModel.js');

var _streamingModelsMediaPlayerModelJs2 = _interopRequireDefault(_streamingModelsMediaPlayerModelJs);

var _streamingUtilsDOMStorageJs = _dereq_('../../streaming/utils/DOMStorage.js');

var _streamingUtilsDOMStorageJs2 = _interopRequireDefault(_streamingUtilsDOMStorageJs);

var _streamingVoErrorJs = _dereq_('../../streaming/vo/Error.js');

var _streamingVoErrorJs2 = _interopRequireDefault(_streamingVoErrorJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function RepresentationController() {

    var SEGMENTS_UPDATE_FAILED_ERROR_CODE = 1;

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        data = undefined,
        dataIndex = undefined,
        updating = undefined,
        availableRepresentations = undefined,
        currentRepresentation = undefined,
        streamProcessor = undefined,
        abrController = undefined,
        indexHandler = undefined,
        streamController = undefined,
        playbackController = undefined,
        manifestModel = undefined,
        metricsModel = undefined,
        domStorage = undefined,
        timelineConverter = undefined,
        dashManifestModel = undefined,
        dashMetrics = undefined,
        mediaPlayerModel = undefined;

    function setup() {
        data = null;
        dataIndex = -1;
        updating = true;
        availableRepresentations = [];

        abrController = (0, _streamingControllersAbrControllerJs2['default'])(context).getInstance();
        streamController = (0, _streamingControllersStreamControllerJs2['default'])(context).getInstance();
        playbackController = (0, _streamingControllersPlaybackControllerJs2['default'])(context).getInstance();
        manifestModel = (0, _streamingModelsManifestModelJs2['default'])(context).getInstance();
        metricsModel = (0, _streamingModelsMetricsModelJs2['default'])(context).getInstance();
        domStorage = (0, _streamingUtilsDOMStorageJs2['default'])(context).getInstance();
        timelineConverter = (0, _utilsTimelineConverterJs2['default'])(context).getInstance();
        dashManifestModel = (0, _modelsDashManifestModelJs2['default'])(context).getInstance();
        dashMetrics = (0, _DashMetricsJs2['default'])(context).getInstance();
        mediaPlayerModel = (0, _streamingModelsMediaPlayerModelJs2['default'])(context).getInstance();

        eventBus.on(_coreEventsEventsJs2['default'].QUALITY_CHANGED, onQualityChanged, instance);
        eventBus.on(_coreEventsEventsJs2['default'].REPRESENTATION_UPDATED, onRepresentationUpdated, instance);
        eventBus.on(_coreEventsEventsJs2['default'].WALLCLOCK_TIME_UPDATED, onWallclockTimeUpdated, instance);
        eventBus.on(_coreEventsEventsJs2['default'].BUFFER_LEVEL_UPDATED, onBufferLevelUpdated, instance);
        eventBus.on(_coreEventsEventsJs2['default'].LIVE_EDGE_SEARCH_COMPLETED, onLiveEdgeSearchCompleted, instance);
    }

    function setConfig(config) {
        // allow the abrController created in setup to be overidden
        if (config.abrController) {
            abrController = config.abrController;
        }
    }

    function initialize(StreamProcessor) {
        streamProcessor = StreamProcessor;
        indexHandler = streamProcessor.getIndexHandler();
    }

    function getStreamProcessor() {
        return streamProcessor;
    }

    function getData() {
        return data;
    }

    function getDataIndex() {
        return dataIndex;
    }

    function isUpdating() {
        return updating;
    }

    function getCurrentRepresentation() {
        return currentRepresentation;
    }

    function reset() {

        eventBus.off(_coreEventsEventsJs2['default'].QUALITY_CHANGED, onQualityChanged, instance);
        eventBus.off(_coreEventsEventsJs2['default'].REPRESENTATION_UPDATED, onRepresentationUpdated, instance);
        eventBus.off(_coreEventsEventsJs2['default'].WALLCLOCK_TIME_UPDATED, onWallclockTimeUpdated, instance);
        eventBus.off(_coreEventsEventsJs2['default'].BUFFER_LEVEL_UPDATED, onBufferLevelUpdated, instance);
        eventBus.off(_coreEventsEventsJs2['default'].LIVE_EDGE_SEARCH_COMPLETED, onLiveEdgeSearchCompleted, instance);

        data = null;
        dataIndex = -1;
        updating = true;
        availableRepresentations = [];
        abrController = null;
        streamController = null;
        playbackController = null;
        manifestModel = null;
        metricsModel = null;
        domStorage = null;
        timelineConverter = null;
        dashManifestModel = null;
        dashMetrics = null;
        mediaPlayerModel = null;
    }

    function updateData(dataValue, adaptation, type) {
        var quality, averageThroughput;

        var bitrate = null;
        var streamInfo = streamProcessor.getStreamInfo();
        var maxQuality = abrController.getTopQualityIndexFor(type, streamInfo.id);

        updating = true;
        eventBus.trigger(_coreEventsEventsJs2['default'].DATA_UPDATE_STARTED, { sender: this });

        availableRepresentations = updateRepresentations(adaptation);

        if (data === null && type !== 'fragmentedText') {
            averageThroughput = abrController.getAverageThroughput(type);
            bitrate = averageThroughput || abrController.getInitialBitrateFor(type, streamInfo);
            quality = abrController.getQualityForBitrate(streamProcessor.getMediaInfo(), bitrate);
        } else {
            quality = abrController.getQualityFor(type, streamInfo);
        }

        if (quality > maxQuality) {
            quality = maxQuality;
        }

        currentRepresentation = getRepresentationForQuality(quality);
        data = dataValue;

        if (type !== 'video' && type !== 'audio' && type !== 'fragmentedText') {
            updating = false;
            eventBus.trigger(_coreEventsEventsJs2['default'].DATA_UPDATE_COMPLETED, { sender: this, data: data, currentRepresentation: currentRepresentation });
            return;
        }

        for (var i = 0; i < availableRepresentations.length; i++) {
            indexHandler.updateRepresentation(availableRepresentations[i], true);
        }
    }

    function addRepresentationSwitch() {
        var now = new Date();
        var currentRepresentation = getCurrentRepresentation();
        var currentVideoTimeMs = playbackController.getTime() * 1000;

        metricsModel.addRepresentationSwitch(currentRepresentation.adaptation.type, now, currentVideoTimeMs, currentRepresentation.id);
    }

    function addDVRMetric() {
        var range = timelineConverter.calcSegmentAvailabilityRange(currentRepresentation, streamProcessor.isDynamic());
        metricsModel.addDVRInfo(streamProcessor.getType(), playbackController.getTime(), streamProcessor.getStreamInfo().manifestInfo, range);
    }

    function getRepresentationForQuality(quality) {
        return availableRepresentations[quality];
    }

    function getQualityForRepresentation(representation) {
        return availableRepresentations.indexOf(representation);
    }

    function isAllRepresentationsUpdated() {
        for (var i = 0, ln = availableRepresentations.length; i < ln; i++) {
            var segmentInfoType = availableRepresentations[i].segmentInfoType;
            if (availableRepresentations[i].segmentAvailabilityRange === null || availableRepresentations[i].initialization === null || (segmentInfoType === 'SegmentBase' || segmentInfoType === 'BaseURL') && !availableRepresentations[i].segments) {
                return false;
            }
        }

        return true;
    }

    function updateRepresentations(adaptation) {
        var reps;
        var manifest = manifestModel.getValue();

        dataIndex = dashManifestModel.getIndexForAdaptation(data, manifest, adaptation.period.index);
        reps = dashManifestModel.getRepresentationsForAdaptation(manifest, adaptation);

        return reps;
    }

    function updateAvailabilityWindow(isDynamic) {
        var rep;

        for (var i = 0, ln = availableRepresentations.length; i < ln; i++) {
            rep = availableRepresentations[i];
            rep.segmentAvailabilityRange = timelineConverter.calcSegmentAvailabilityRange(rep, isDynamic);
        }
    }

    function resetAvailabilityWindow() {
        availableRepresentations.forEach(function (rep) {
            rep.segmentAvailabilityRange = null;
        });
    }

    function postponeUpdate(postponeTimePeriod) {
        var delay = postponeTimePeriod;
        var update = function update() {
            if (isUpdating()) return;

            updating = true;
            eventBus.trigger(_coreEventsEventsJs2['default'].DATA_UPDATE_STARTED, { sender: instance });

            // clear the segmentAvailabilityRange for all reps.
            // this ensures all are updated before the live edge search starts
            resetAvailabilityWindow();

            for (var i = 0; i < availableRepresentations.length; i++) {
                indexHandler.updateRepresentation(availableRepresentations[i], true);
            }
        };

        updating = false;
        eventBus.trigger(_coreEventsEventsJs2['default'].AST_IN_FUTURE, { delay: delay });
        setTimeout(update, delay);
    }

    function onRepresentationUpdated(e) {
        if (e.sender.getStreamProcessor() !== streamProcessor || !isUpdating()) return;

        var r = e.representation;
        var streamMetrics = metricsModel.getMetricsFor('stream');
        var metrics = metricsModel.getMetricsFor(getCurrentRepresentation().adaptation.type);
        var manifestUpdateInfo = dashMetrics.getCurrentManifestUpdate(streamMetrics);

        var repInfo, err, repSwitch;
        var alreadyAdded = false;

        var postponeTimePeriod = 0;

        if (r.adaptation.period.mpd.manifest.type == 'dynamic') {
            var segmentAvailabilityTimePeriod = r.segmentAvailabilityRange.end - r.segmentAvailabilityRange.start;
            // We must put things to sleep unless till e.g. the startTime calculation in ScheduleController.onLiveEdgeSearchCompleted fall after the segmentAvailabilityRange.start
            var liveDelay = mediaPlayerModel.getLiveDelay() || currentRepresentation.segmentDuration * mediaPlayerModel.getLiveDelayFragmentCount();
            postponeTimePeriod = (liveDelay - segmentAvailabilityTimePeriod) * 1000;
        }

        if (postponeTimePeriod > 0) {
            addDVRMetric();
            postponeUpdate(postponeTimePeriod);
            err = new _streamingVoErrorJs2['default'](SEGMENTS_UPDATE_FAILED_ERROR_CODE, 'Segments update failed', null);
            eventBus.trigger(_coreEventsEventsJs2['default'].DATA_UPDATE_COMPLETED, { sender: this, data: data, currentRepresentation: currentRepresentation, error: err });

            return;
        }

        if (manifestUpdateInfo) {
            for (var i = 0; i < manifestUpdateInfo.trackInfo.length; i++) {
                repInfo = manifestUpdateInfo.trackInfo[i];
                if (repInfo.index === r.index && repInfo.mediaType === streamProcessor.getType()) {
                    alreadyAdded = true;
                    break;
                }
            }

            if (!alreadyAdded) {
                metricsModel.addManifestUpdateRepresentationInfo(manifestUpdateInfo, r.id, r.index, r.adaptation.period.index, streamProcessor.getType(), r.presentationTimeOffset, r.startNumber, r.segmentInfoType);
            }
        }

        if (isAllRepresentationsUpdated()) {
            updating = false;
            abrController.setPlaybackQuality(streamProcessor.getType(), streamProcessor.getStreamInfo(), getQualityForRepresentation(currentRepresentation));
            metricsModel.updateManifestUpdateInfo(manifestUpdateInfo, { latency: currentRepresentation.segmentAvailabilityRange.end - playbackController.getTime() });

            repSwitch = dashMetrics.getCurrentRepresentationSwitch(metrics);

            if (!repSwitch) {
                addRepresentationSwitch();
            }

            eventBus.trigger(_coreEventsEventsJs2['default'].DATA_UPDATE_COMPLETED, { sender: this, data: data, currentRepresentation: currentRepresentation });
        }
    }

    function onWallclockTimeUpdated(e) {
        if (e.isDynamic) {
            updateAvailabilityWindow(e.isDynamic);
        }
    }

    function onLiveEdgeSearchCompleted(e) {
        if (e.error) return;

        updateAvailabilityWindow(true);
        indexHandler.updateRepresentation(currentRepresentation, false);

        // we need to update checkTime after we have found the live edge because its initial value
        // does not take into account clientServerTimeShift
        var manifest = manifestModel.getValue();
        var period = currentRepresentation.adaptation.period;
        var streamInfo = streamController.getActiveStreamInfo();

        if (streamInfo.isLast) {
            period.mpd.checkTime = dashManifestModel.getCheckTime(manifest, period);
            period.duration = dashManifestModel.getEndTimeForLastPeriod(manifestModel.getValue(), period) - period.start;
            streamInfo.duration = period.duration;
        }
    }

    function onBufferLevelUpdated(e) {
        if (e.sender.getStreamProcessor() !== streamProcessor) return;
        addDVRMetric();
    }

    function onQualityChanged(e) {
        if (e.mediaType !== streamProcessor.getType() || streamProcessor.getStreamInfo().id !== e.streamInfo.id) return;

        if (e.oldQuality !== e.newQuality) {
            currentRepresentation = getRepresentationForQuality(e.newQuality);
            domStorage.setSavedBitrateSettings(e.mediaType, currentRepresentation.bandwidth);
            addRepresentationSwitch();
        }
    }

    instance = {
        initialize: initialize,
        setConfig: setConfig,
        getData: getData,
        getDataIndex: getDataIndex,
        isUpdating: isUpdating,
        updateData: updateData,
        getStreamProcessor: getStreamProcessor,
        getCurrentRepresentation: getCurrentRepresentation,
        getRepresentationForQuality: getRepresentationForQuality,
        reset: reset
    };

    setup();
    return instance;
}

RepresentationController.__dashjs_factory_name = 'RepresentationController';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(RepresentationController);
module.exports = exports['default'];

},{"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../../streaming/controllers/AbrController.js":46,"../../streaming/controllers/PlaybackController.js":54,"../../streaming/controllers/StreamController.js":57,"../../streaming/models/ManifestModel.js":63,"../../streaming/models/MediaPlayerModel.js":64,"../../streaming/models/MetricsModel.js":65,"../../streaming/utils/DOMStorage.js":89,"../../streaming/vo/Error.js":100,"../DashMetrics.js":13,"../models/DashManifestModel.js":17,"../utils/TimelineConverter.js":23}],17:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _voRepresentationJs = _dereq_('../vo/Representation.js');

var _voRepresentationJs2 = _interopRequireDefault(_voRepresentationJs);

var _voAdaptationSetJs = _dereq_('../vo/AdaptationSet.js');

var _voAdaptationSetJs2 = _interopRequireDefault(_voAdaptationSetJs);

var _voPeriodJs = _dereq_('../vo/Period.js');

var _voPeriodJs2 = _interopRequireDefault(_voPeriodJs);

var _voMpdJs = _dereq_('../vo/Mpd.js');

var _voMpdJs2 = _interopRequireDefault(_voMpdJs);

var _voUTCTimingJs = _dereq_('../vo/UTCTiming.js');

var _voUTCTimingJs2 = _interopRequireDefault(_voUTCTimingJs);

var _utilsTimelineConverterJs = _dereq_('../utils/TimelineConverter.js');

var _utilsTimelineConverterJs2 = _interopRequireDefault(_utilsTimelineConverterJs);

var _voEventJs = _dereq_('../vo/Event.js');

var _voEventJs2 = _interopRequireDefault(_voEventJs);

var _voBaseURLJs = _dereq_('../vo/BaseURL.js');

var _voBaseURLJs2 = _interopRequireDefault(_voBaseURLJs);

var _voEventStreamJs = _dereq_('../vo/EventStream.js');

var _voEventStreamJs2 = _interopRequireDefault(_voEventStreamJs);

var _streamingUtilsURLUtilsJs = _dereq_('../../streaming/utils/URLUtils.js');

var _streamingUtilsURLUtilsJs2 = _interopRequireDefault(_streamingUtilsURLUtilsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function DashManifestModel() {

    var instance = undefined;
    var context = this.context;
    var timelineConverter = (0, _utilsTimelineConverterJs2['default'])(context).getInstance(); //TODO Need to pass this in not bake in
    var urlUtils = (0, _streamingUtilsURLUtilsJs2['default'])(context).getInstance();

    function getIsTypeOf(adaptation, type) {

        var i, len, representation;
        var result = false;
        var found = false;

        var col = adaptation.ContentComponent_asArray;
        var mimeTypeRegEx = type !== 'text' ? new RegExp(type) : new RegExp('(vtt|ttml)');

        if (adaptation.Representation_asArray.length > 0 && adaptation.Representation_asArray[0].hasOwnProperty('codecs')) {
            var codecs = adaptation.Representation_asArray[0].codecs;
            if (codecs === 'stpp' || codecs === 'wvtt') {
                return type === 'fragmentedText';
            }
        }

        if (col) {
            if (col.length > 1) {
                return type == 'muxed';
            } else if (col[0] && col[0].contentType === type) {
                result = true;
                found = true;
            }
        }

        if (adaptation.hasOwnProperty('mimeType')) {
            result = mimeTypeRegEx.test(adaptation.mimeType);
            found = true;
        }

        // couldn't find on adaptationset, so check a representation
        if (!found) {
            i = 0;
            len = adaptation.Representation_asArray.length;
            while (!found && i < len) {
                representation = adaptation.Representation_asArray[i];

                if (representation.hasOwnProperty('mimeType')) {
                    result = mimeTypeRegEx.test(representation.mimeType);
                    found = true;
                }

                i++;
            }
        }

        return result;
    }

    function getIsAudio(adaptation) {
        return getIsTypeOf(adaptation, 'audio');
    }

    function getIsVideo(adaptation) {
        return getIsTypeOf(adaptation, 'video');
    }

    function getIsFragmentedText(adaptation) {
        return getIsTypeOf(adaptation, 'fragmentedText');
    }

    function getIsText(adaptation) {
        return getIsTypeOf(adaptation, 'text');
    }

    function getIsMuxed(adaptation) {
        return getIsTypeOf(adaptation, 'muxed');
    }

    function getIsTextTrack(type) {
        return type === 'text/vtt' || type === 'application/ttml+xml';
    }

    function getLanguageForAdaptation(adaptation) {
        var lang = '';

        if (adaptation.hasOwnProperty('lang')) {
            //Filter out any other characters not allowed according to RFC5646
            lang = adaptation.lang.replace(/[^A-Za-z0-9-]/g, '');
        }

        return lang;
    }

    function getViewpointForAdaptation(adaptation) {
        return adaptation.hasOwnProperty('Viewpoint') ? adaptation.Viewpoint : null;
    }

    function getRolesForAdaptation(adaptation) {
        return adaptation.hasOwnProperty('Role_asArray') ? adaptation.Role_asArray : [];
    }

    function getAccessibilityForAdaptation(adaptation) {
        return adaptation.hasOwnProperty('Accessibility_asArray') ? adaptation.Accessibility_asArray : [];
    }

    function getAudioChannelConfigurationForAdaptation(adaptation) {
        return adaptation.hasOwnProperty('AudioChannelConfiguration_asArray') ? adaptation.AudioChannelConfiguration_asArray : [];
    }

    function getIsMain(adaptation) {
        return getRolesForAdaptation(adaptation).filter(function (role) {
            return role.value === 'main';
        })[0];
    }

    function getRepresentationSortFunction() {
        return function (a, b) {
            return a.bandwidth - b.bandwidth;
        };
    }

    function processAdaptation(adaptation) {
        if (adaptation.Representation_asArray !== undefined && adaptation.Representation_asArray !== null) {
            adaptation.Representation_asArray.sort(getRepresentationSortFunction());
        }

        return adaptation;
    }

    function getAdaptationForId(id, manifest, periodIndex) {

        var adaptations = manifest.Period_asArray[periodIndex].AdaptationSet_asArray;
        var i, len;

        for (i = 0, len = adaptations.length; i < len; i++) {
            if (adaptations[i].hasOwnProperty('id') && adaptations[i].id === id) {
                return adaptations[i];
            }
        }

        return null;
    }

    function getAdaptationForIndex(index, manifest, periodIndex) {
        var adaptations = manifest.Period_asArray[periodIndex].AdaptationSet_asArray;
        return adaptations[index];
    }

    function getIndexForAdaptation(adaptation, manifest, periodIndex) {

        var adaptations = manifest.Period_asArray[periodIndex].AdaptationSet_asArray;
        var i, len;

        for (i = 0, len = adaptations.length; i < len; i++) {
            if (adaptations[i] === adaptation) {
                return i;
            }
        }

        return -1;
    }

    function getAdaptationsForType(manifest, periodIndex, type) {

        var adaptationSet = manifest.Period_asArray[periodIndex].AdaptationSet_asArray;
        var i, len;
        var adaptations = [];

        for (i = 0, len = adaptationSet.length; i < len; i++) {
            if (getIsTypeOf(adaptationSet[i], type)) {
                adaptations.push(processAdaptation(adaptationSet[i]));
            }
        }

        return adaptations;
    }

    function getAdaptationForType(manifest, periodIndex, type) {
        var i, len, adaptations;

        adaptations = getAdaptationsForType(manifest, periodIndex, type);

        if (!adaptations || adaptations.length === 0) return null;

        for (i = 0, len = adaptations.length; i < len; i++) {
            if (getIsMain(adaptations[i])) return adaptations[i];
        }

        return adaptations[0];
    }

    function getCodec(adaptation) {
        var representation = adaptation.Representation_asArray[0];
        return representation.mimeType + ';codecs="' + representation.codecs + '"';
    }

    function getMimeType(adaptation) {
        return adaptation.Representation_asArray[0].mimeType;
    }

    function getKID(adaptation) {
        if (!adaptation || !adaptation.hasOwnProperty('cenc:default_KID')) {
            return null;
        }
        return adaptation['cenc:default_KID'];
    }

    function getContentProtectionData(adaptation) {
        if (!adaptation || !adaptation.hasOwnProperty('ContentProtection_asArray') || adaptation.ContentProtection_asArray.length === 0) {
            return null;
        }
        return adaptation.ContentProtection_asArray;
    }

    function getIsDynamic(manifest) {
        var isDynamic = false;
        if (manifest.hasOwnProperty('type')) {
            isDynamic = manifest.type === 'dynamic';
        }
        return isDynamic;
    }

    function getIsDVR(manifest) {
        var isDynamic = getIsDynamic(manifest);
        var containsDVR, isDVR;

        containsDVR = !isNaN(manifest.timeShiftBufferDepth);
        isDVR = isDynamic && containsDVR;

        return isDVR;
    }

    function hasProfile(manifest, profile) {
        var has = false;

        if (manifest.profiles && manifest.profiles.length > 0) {
            has = manifest.profiles.indexOf(profile) !== -1;
        }

        return has;
    }

    function getIsOnDemand(manifest) {
        return hasProfile(manifest, 'urn:mpeg:dash:profile:isoff-on-demand:2011');
    }

    function getIsDVB(manifest) {
        return hasProfile(manifest, 'urn:dvb:dash:profile:dvb-dash:2014');
    }

    function getDuration(manifest) {
        var mpdDuration;
        //@mediaPresentationDuration specifies the duration of the entire Media Presentation.
        //If the attribute is not present, the duration of the Media Presentation is unknown.
        if (manifest.hasOwnProperty('mediaPresentationDuration')) {
            mpdDuration = manifest.mediaPresentationDuration;
        } else {
            mpdDuration = Number.MAX_VALUE;
        }

        return mpdDuration;
    }

    function getBandwidth(representation) {
        return representation.bandwidth;
    }

    function getRefreshDelay(manifest) {
        var delay = NaN;
        var minDelay = 2;

        if (manifest.hasOwnProperty('minimumUpdatePeriod')) {
            delay = Math.max(parseFloat(manifest.minimumUpdatePeriod), minDelay);
        }

        return delay;
    }

    function getRepresentationCount(adaptation) {
        return adaptation.Representation_asArray.length;
    }

    function getBitrateListForAdaptation(adaptation) {
        if (!adaptation || !adaptation.Representation_asArray || !adaptation.Representation_asArray.length) return null;

        var a = processAdaptation(adaptation);
        var reps = a.Representation_asArray;
        var ln = reps.length;
        var bitrateList = [];

        for (var i = 0; i < ln; i++) {
            bitrateList.push({
                bandwidth: reps[i].bandwidth,
                width: reps[i].width || 0,
                height: reps[i].height || 0
            });
        }

        return bitrateList;
    }

    function getRepresentationFor(index, adaptation) {
        return adaptation.Representation_asArray[index];
    }

    function getRepresentationsForAdaptation(manifest, adaptation) {
        var a = processAdaptation(manifest.Period_asArray[adaptation.period.index].AdaptationSet_asArray[adaptation.index]);
        var representations = [];
        var representation, initialization, segmentInfo, r, s;

        for (var i = 0; i < a.Representation_asArray.length; i++) {
            r = a.Representation_asArray[i];
            representation = new _voRepresentationJs2['default']();
            representation.index = i;
            representation.adaptation = adaptation;

            if (r.hasOwnProperty('id')) {
                representation.id = r.id;
            }

            if (r.hasOwnProperty('bandwidth')) {
                representation.bandwidth = r.bandwidth;
            }
            if (r.hasOwnProperty('maxPlayoutRate')) {
                representation.maxPlayoutRate = r.maxPlayoutRate;
            }
            if (r.hasOwnProperty('SegmentBase')) {
                segmentInfo = r.SegmentBase;
                representation.segmentInfoType = 'SegmentBase';
            } else if (r.hasOwnProperty('SegmentList')) {
                segmentInfo = r.SegmentList;
                representation.segmentInfoType = 'SegmentList';
                representation.useCalculatedLiveEdgeTime = true;
            } else if (r.hasOwnProperty('SegmentTemplate')) {
                segmentInfo = r.SegmentTemplate;

                if (segmentInfo.hasOwnProperty('SegmentTimeline')) {
                    representation.segmentInfoType = 'SegmentTimeline';
                    s = segmentInfo.SegmentTimeline.S_asArray[segmentInfo.SegmentTimeline.S_asArray.length - 1];
                    if (!s.hasOwnProperty('r') || s.r >= 0) {
                        representation.useCalculatedLiveEdgeTime = true;
                    }
                } else {
                    representation.segmentInfoType = 'SegmentTemplate';
                }

                if (segmentInfo.hasOwnProperty('initialization')) {
                    representation.initialization = segmentInfo.initialization.split('$Bandwidth$').join(r.bandwidth).split('$RepresentationID$').join(r.id);
                }
            } else {
                segmentInfo = r.BaseURL;
                representation.segmentInfoType = 'BaseURL';
            }

            if (segmentInfo.hasOwnProperty('Initialization')) {
                initialization = segmentInfo.Initialization;
                if (initialization.hasOwnProperty('sourceURL')) {
                    representation.initialization = initialization.sourceURL;
                } else if (initialization.hasOwnProperty('range')) {
                    representation.range = initialization.range;
                }
            } else if (r.hasOwnProperty('mimeType') && getIsTextTrack(r.mimeType)) {
                representation.range = 0;
            }

            if (segmentInfo.hasOwnProperty('timescale')) {
                representation.timescale = segmentInfo.timescale;
            }
            if (segmentInfo.hasOwnProperty('duration')) {
                // TODO according to the spec @maxSegmentDuration specifies the maximum duration of any Segment in any Representation in the Media Presentation
                // It is also said that for a SegmentTimeline any @d value shall not exceed the value of MPD@maxSegmentDuration, but nothing is said about
                // SegmentTemplate @duration attribute. We need to find out if @maxSegmentDuration should be used instead of calculated duration if the the duration
                // exceeds @maxSegmentDuration
                //representation.segmentDuration = Math.min(segmentInfo.duration / representation.timescale, adaptation.period.mpd.maxSegmentDuration);
                representation.segmentDuration = segmentInfo.duration / representation.timescale;
            }
            if (segmentInfo.hasOwnProperty('startNumber')) {
                representation.startNumber = segmentInfo.startNumber;
            }
            if (segmentInfo.hasOwnProperty('indexRange')) {
                representation.indexRange = segmentInfo.indexRange;
            }
            if (segmentInfo.hasOwnProperty('presentationTimeOffset')) {
                representation.presentationTimeOffset = segmentInfo.presentationTimeOffset / representation.timescale;
            }

            representation.MSETimeOffset = timelineConverter.calcMSETimeOffset(representation);

            representation.path = [adaptation.period.index, adaptation.index, i];

            representations.push(representation);
        }

        return representations;
    }

    function getAdaptationsForPeriod(manifest, period) {
        var p = manifest.Period_asArray[period.index];
        var adaptations = [];
        var adaptationSet, a;

        for (var i = 0; i < p.AdaptationSet_asArray.length; i++) {
            a = p.AdaptationSet_asArray[i];
            adaptationSet = new _voAdaptationSetJs2['default']();

            if (a.hasOwnProperty('id')) {
                adaptationSet.id = a.id;
            }

            adaptationSet.index = i;
            adaptationSet.period = period;

            if (getIsMuxed(a)) {
                adaptationSet.type = 'muxed';
            } else if (getIsAudio(a)) {
                adaptationSet.type = 'audio';
            } else if (getIsVideo(a)) {
                adaptationSet.type = 'video';
            } else if (getIsFragmentedText(a)) {
                adaptationSet.type = 'fragmentedText';
            } else {
                adaptationSet.type = 'text';
            }

            adaptations.push(adaptationSet);
        }

        return adaptations;
    }

    function getRegularPeriods(manifest, mpd) {
        var isDynamic = getIsDynamic(manifest);
        var periods = [];
        var p1 = null;
        var p = null;
        var vo1 = null;
        var vo = null;
        var len, i;

        for (i = 0, len = manifest.Period_asArray.length; i < len; i++) {
            p = manifest.Period_asArray[i];

            // If the attribute @start is present in the Period, then the
            // Period is a regular Period and the PeriodStart is equal
            // to the value of this attribute.
            if (p.hasOwnProperty('start')) {
                vo = new _voPeriodJs2['default']();
                vo.start = p.start;
            }
            // If the @start attribute is absent, but the previous Period
            // element contains a @duration attribute then then this new
            // Period is also a regular Period. The start time of the new
            // Period PeriodStart is the sum of the start time of the previous
            // Period PeriodStart and the value of the attribute @duration
            // of the previous Period.
            else if (p1 !== null && p.hasOwnProperty('duration') && vo1 !== null) {
                    vo = new _voPeriodJs2['default']();
                    vo.start = vo1.start + vo1.duration;
                    vo.duration = p.duration;
                }
                // If (i) @start attribute is absent, and (ii) the Period element
                // is the first in the MPD, and (iii) the MPD@type is 'static',
                // then the PeriodStart time shall be set to zero.
                else if (i === 0 && !isDynamic) {
                        vo = new _voPeriodJs2['default']();
                        vo.start = 0;
                    }

            // The Period extends until the PeriodStart of the next Period.
            // The difference between the PeriodStart time of a Period and
            // the PeriodStart time of the following Period.
            if (vo1 !== null && isNaN(vo1.duration)) {
                vo1.duration = vo.start - vo1.start;
            }

            if (vo !== null) {
                vo.id = getPeriodId(p);
            }

            if (vo !== null && p.hasOwnProperty('duration')) {
                vo.duration = p.duration;
            }

            if (vo !== null) {
                vo.index = i;
                vo.mpd = mpd;
                periods.push(vo);
                p1 = p;
                vo1 = vo;
            }

            p = null;
            vo = null;
        }

        if (periods.length === 0) {
            return periods;
        }

        // The last Period extends until the end of the Media Presentation.
        // The difference between the PeriodStart time of the last Period
        // and the mpd duration
        if (vo1 !== null && isNaN(vo1.duration)) {
            vo1.duration = getEndTimeForLastPeriod(manifest, vo1) - vo1.start;
        }

        return periods;
    }

    function getPeriodId(p) {
        if (!p) {
            throw new Error('Period cannot be null or undefined');
        }

        var id = _voPeriodJs2['default'].DEFAULT_ID;

        if (p.hasOwnProperty('id') && p.id !== '__proto__') {
            id = p.id;
        }

        return id;
    }

    function getMpd(manifest) {
        var mpd = new _voMpdJs2['default']();

        mpd.manifest = manifest;

        if (manifest.hasOwnProperty('availabilityStartTime')) {
            mpd.availabilityStartTime = new Date(manifest.availabilityStartTime.getTime());
        } else {
            mpd.availabilityStartTime = new Date(manifest.loadedTime.getTime());
        }

        if (manifest.hasOwnProperty('availabilityEndTime')) {
            mpd.availabilityEndTime = new Date(manifest.availabilityEndTime.getTime());
        }

        if (manifest.hasOwnProperty('suggestedPresentationDelay')) {
            mpd.suggestedPresentationDelay = manifest.suggestedPresentationDelay;
        }

        if (manifest.hasOwnProperty('timeShiftBufferDepth')) {
            mpd.timeShiftBufferDepth = manifest.timeShiftBufferDepth;
        }

        if (manifest.hasOwnProperty('maxSegmentDuration')) {
            mpd.maxSegmentDuration = manifest.maxSegmentDuration;
        }

        return mpd;
    }

    function getFetchTime(manifest, period) {
        // FetchTime is defined as the time at which the server processes the request for the MPD from the client.
        // TODO The client typically should not use the time at which it actually successfully received the MPD, but should
        // take into account delay due to MPD delivery and processing. The fetch is considered successful fetching
        // either if the client obtains an updated MPD or the client verifies that the MPD has not been updated since the previous fetching.

        return timelineConverter.calcPresentationTimeFromWallTime(manifest.loadedTime, period);
    }

    function getCheckTime(manifest, period) {
        var checkTime = NaN;
        var fetchTime;

        // If the MPD@minimumUpdatePeriod attribute in the client is provided, then the check time is defined as the
        // sum of the fetch time of this operating MPD and the value of this attribute,
        // i.e. CheckTime = FetchTime + MPD@minimumUpdatePeriod.
        if (manifest.hasOwnProperty('minimumUpdatePeriod')) {
            fetchTime = getFetchTime(manifest, period);
            checkTime = fetchTime + manifest.minimumUpdatePeriod;
        }
        // TODO If the MPD@minimumUpdatePeriod attribute in the client is not provided, external means are used to
        // determine CheckTime, such as a priori knowledge, or HTTP cache headers, etc.

        return checkTime;
    }

    function getEndTimeForLastPeriod(manifest, period) {
        var periodEnd;
        var checkTime = getCheckTime(manifest, period);

        // if the MPD@mediaPresentationDuration attribute is present, then PeriodEndTime is defined as the end time of the Media Presentation.
        // if the MPD@mediaPresentationDuration attribute is not present, then PeriodEndTime is defined as FetchTime + MPD@minimumUpdatePeriod

        if (manifest.mediaPresentationDuration) {
            periodEnd = manifest.mediaPresentationDuration;
        } else if (!isNaN(checkTime)) {
            // in this case the Period End Time should match CheckTime
            periodEnd = checkTime;
        } else {
            throw new Error('Must have @mediaPresentationDuration or @minimumUpdatePeriod on MPD or an explicit @duration on the last period.');
        }

        return periodEnd;
    }

    function getEventsForPeriod(manifest, period) {

        var periodArray = manifest.Period_asArray;
        var eventStreams = periodArray[period.index].EventStream_asArray;
        var events = [];

        if (eventStreams) {
            for (var i = 0; i < eventStreams.length; i++) {
                var eventStream = new _voEventStreamJs2['default']();
                eventStream.period = period;
                eventStream.timescale = 1;

                if (eventStreams[i].hasOwnProperty('schemeIdUri')) {
                    eventStream.schemeIdUri = eventStreams[i].schemeIdUri;
                } else {
                    throw 'Invalid EventStream. SchemeIdUri has to be set';
                }
                if (eventStreams[i].hasOwnProperty('timescale')) {
                    eventStream.timescale = eventStreams[i].timescale;
                }
                if (eventStreams[i].hasOwnProperty('value')) {
                    eventStream.value = eventStreams[i].value;
                }
                for (var j = 0; j < eventStreams[i].Event_asArray.length; j++) {
                    var event = new _voEventJs2['default']();
                    event.presentationTime = 0;
                    event.eventStream = eventStream;

                    if (eventStreams[i].Event_asArray[j].hasOwnProperty('presentationTime')) {
                        event.presentationTime = eventStreams[i].Event_asArray[j].presentationTime;
                    }
                    if (eventStreams[i].Event_asArray[j].hasOwnProperty('duration')) {
                        event.duration = eventStreams[i].Event_asArray[j].duration;
                    }
                    if (eventStreams[i].Event_asArray[j].hasOwnProperty('id')) {
                        event.id = eventStreams[i].Event_asArray[j].id;
                    }
                    events.push(event);
                }
            }
        }

        return events;
    }

    function getEventStreams(inbandStreams, representation) {
        var eventStreams = [];

        if (!inbandStreams) return eventStreams;

        for (var i = 0; i < inbandStreams.length; i++) {
            var eventStream = new _voEventStreamJs2['default']();
            eventStream.timescale = 1;
            eventStream.representation = representation;

            if (inbandStreams[i].hasOwnProperty('schemeIdUri')) {
                eventStream.schemeIdUri = inbandStreams[i].schemeIdUri;
            } else {
                throw 'Invalid EventStream. SchemeIdUri has to be set';
            }
            if (inbandStreams[i].hasOwnProperty('timescale')) {
                eventStream.timescale = inbandStreams[i].timescale;
            }
            if (inbandStreams[i].hasOwnProperty('value')) {
                eventStream.value = inbandStreams[i].value;
            }
            eventStreams.push(eventStream);
        }

        return eventStreams;
    }

    function getEventStreamForAdaptationSet(manifest, adaptation) {
        var inbandStreams = manifest.Period_asArray[adaptation.period.index].AdaptationSet_asArray[adaptation.index].InbandEventStream_asArray;

        return getEventStreams(inbandStreams, null);
    }

    function getEventStreamForRepresentation(manifest, representation) {
        var inbandStreams = manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].InbandEventStream_asArray;

        return getEventStreams(inbandStreams, representation);
    }

    function getUTCTimingSources(manifest) {

        var isDynamic = getIsDynamic(manifest);
        var hasAST = manifest.hasOwnProperty('availabilityStartTime');
        var utcTimingsArray = manifest.UTCTiming_asArray;
        var utcTimingEntries = [];

        // do not bother synchronizing the clock unless MPD is live,
        // or it is static and has availabilityStartTime attribute
        if (isDynamic || hasAST) {
            if (utcTimingsArray) {
                // the order is important here - 23009-1 states that the order
                // in the manifest "indicates relative preference, first having
                // the highest, and the last the lowest priority".
                utcTimingsArray.forEach(function (utcTiming) {
                    var entry = new _voUTCTimingJs2['default']();

                    if (utcTiming.hasOwnProperty('schemeIdUri')) {
                        entry.schemeIdUri = utcTiming.schemeIdUri;
                    } else {
                        // entries of type DescriptorType with no schemeIdUri
                        // are meaningless. let's just ignore this entry and
                        // move on.
                        return;
                    }

                    // this is (incorrectly) interpreted as a number - schema
                    // defines it as a string
                    if (utcTiming.hasOwnProperty('value')) {
                        entry.value = utcTiming.value.toString();
                    } else {
                        // without a value, there's not a lot we can do with
                        // this entry. let's just ignore this one and move on
                        return;
                    }

                    // we're not interested in the optional id or any other
                    // attributes which might be attached to the entry

                    utcTimingEntries.push(entry);
                });
            }
        }

        return utcTimingEntries;
    }

    function getBaseURLsFromElement(node) {
        var baseUrls = [];
        var entries = node.BaseURL_asArray || [node.baseUri] || [];

        entries.some(function (entry) {
            if (entry) {
                var baseUrl = new _voBaseURLJs2['default']();
                var text = entry.__text || entry;

                baseUrl.url = text;

                // serviceLocation is optional, but we need it in order
                // to blacklist correctly. if it's not available, use
                // anything unique since there's no relationship to any
                // other BaseURL and, in theory, the url should be
                // unique so use this instead.
                if (entry.hasOwnProperty('serviceLocation') && entry.serviceLocation.length) {
                    baseUrl.serviceLocation = entry.serviceLocation;
                } else {
                    baseUrl.serviceLocation = text;
                }

                if (entry.hasOwnProperty('dvb:priority')) {
                    baseUrl.dvb_priority = entry['dvb:priority'];
                }

                if (entry.hasOwnProperty('dvb:weight')) {
                    baseUrl.dvb_weight = entry['dvb:weight'];
                }

                /* NOTE: byteRange, availabilityTimeOffset,
                 * availabilityTimeComplete currently unused
                 */

                baseUrls.push(baseUrl);

                if (urlUtils.isRelative(text)) {
                    // it doesn't really make sense to have relative and
                    // absolute URLs at the same level, or multiple
                    // relative URLs at the same level, so assume we are
                    // done from this level of the MPD
                    return true;
                }
            }
        });

        return baseUrls;
    }

    instance = {
        getIsTypeOf: getIsTypeOf,
        getIsAudio: getIsAudio,
        getIsVideo: getIsVideo,
        getIsText: getIsText,
        getIsMuxed: getIsMuxed,
        getIsTextTrack: getIsTextTrack,
        getIsFragmentedText: getIsFragmentedText,
        getIsMain: getIsMain,
        getLanguageForAdaptation: getLanguageForAdaptation,
        getViewpointForAdaptation: getViewpointForAdaptation,
        getRolesForAdaptation: getRolesForAdaptation,
        getAccessibilityForAdaptation: getAccessibilityForAdaptation,
        getAudioChannelConfigurationForAdaptation: getAudioChannelConfigurationForAdaptation,
        processAdaptation: processAdaptation,
        getAdaptationForIndex: getAdaptationForIndex,
        getIndexForAdaptation: getIndexForAdaptation,
        getAdaptationForId: getAdaptationForId,
        getAdaptationsForType: getAdaptationsForType,
        getAdaptationForType: getAdaptationForType,
        getCodec: getCodec,
        getMimeType: getMimeType,
        getKID: getKID,
        getContentProtectionData: getContentProtectionData,
        getIsDynamic: getIsDynamic,
        getIsDVR: getIsDVR,
        getIsOnDemand: getIsOnDemand,
        getIsDVB: getIsDVB,
        getDuration: getDuration,
        getBandwidth: getBandwidth,
        getRefreshDelay: getRefreshDelay,
        getRepresentationCount: getRepresentationCount,
        getBitrateListForAdaptation: getBitrateListForAdaptation,
        getRepresentationFor: getRepresentationFor,
        getRepresentationsForAdaptation: getRepresentationsForAdaptation,
        getAdaptationsForPeriod: getAdaptationsForPeriod,
        getRegularPeriods: getRegularPeriods,
        getPeriodId: getPeriodId,
        getMpd: getMpd,
        getFetchTime: getFetchTime,
        getCheckTime: getCheckTime,
        getEndTimeForLastPeriod: getEndTimeForLastPeriod,
        getEventsForPeriod: getEventsForPeriod,
        getEventStreams: getEventStreams,
        getEventStreamForAdaptationSet: getEventStreamForAdaptationSet,
        getEventStreamForRepresentation: getEventStreamForRepresentation,
        getUTCTimingSources: getUTCTimingSources,
        getBaseURLsFromElement: getBaseURLsFromElement,
        getRepresentationSortFunction: getRepresentationSortFunction
    };

    return instance;
}

DashManifestModel.__dashjs_factory_name = 'DashManifestModel';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(DashManifestModel);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7,"../../streaming/utils/URLUtils.js":96,"../utils/TimelineConverter.js":23,"../vo/AdaptationSet.js":25,"../vo/BaseURL.js":26,"../vo/Event.js":27,"../vo/EventStream.js":28,"../vo/Mpd.js":29,"../vo/Period.js":30,"../vo/Representation.js":31,"../vo/UTCTiming.js":33}],18:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function FragmentedTextBoxParser() {

    var instance = undefined,
        boxParser = undefined;

    function setConfig(config) {
        if (!config) return;

        if (config.boxParser) {
            boxParser = config.boxParser;
        }
    }

    function getSamplesInfo(ab) {
        var isoFile = boxParser.parse(ab);
        var tfhdBox = isoFile.getBox('tfhd');
        var tfdtBox = isoFile.getBox('tfdt');
        var trunBox = isoFile.getBox('trun');
        var moofBox = isoFile.getBox('moof');
        var mfhdBox = isoFile.getBox('mfhd');

        var sampleDuration, sampleCompositionTimeOffset, sampleCount, sampleSize, sampleDts, sampleList, sample, i, dataOffset, sequenceNumber, totalDuration;

        sequenceNumber = mfhdBox.sequence_number;
        sampleCount = trunBox.sample_count;
        sampleDts = tfdtBox.baseMediaDecodeTime;
        dataOffset = (tfhdBox.base_data_offset || 0) + (trunBox.data_offset || 0);

        sampleList = [];
        for (i = 0; i < sampleCount; i++) {
            sample = trunBox.samples[i];
            sampleDuration = sample.sample_duration !== undefined ? sample.sample_duration : tfhdBox.default_sample_duration;
            sampleSize = sample.sample_size !== undefined ? sample.sample_size : tfhdBox.default_sample_size;
            sampleCompositionTimeOffset = sample.sample_composition_time_offset !== undefined ? sample.sample_composition_time_offset : 0;

            sampleList.push({ 'dts': sampleDts,
                'cts': sampleDts + sampleCompositionTimeOffset,
                'duration': sampleDuration,
                'offset': moofBox.offset + dataOffset,
                'size': sampleSize });
            dataOffset += sampleSize;
            sampleDts += sampleDuration;
        }
        totalDuration = sampleDts - tfdtBox.baseMediaDecodeTime;
        return { sampleList: sampleList, sequenceNumber: sequenceNumber, totalDuration: totalDuration };
    }

    function getMediaTimescaleFromMoov(ab) {
        var isoFile = boxParser.parse(ab);
        var mdhdBox = isoFile.getBox('mdhd');

        return mdhdBox ? mdhdBox.timescale : NaN;
    }

    instance = {
        getSamplesInfo: getSamplesInfo,
        getMediaTimescaleFromMoov: getMediaTimescaleFromMoov,
        setConfig: setConfig
    };

    return instance;
}

FragmentedTextBoxParser.__dashjs_factory_name = 'FragmentedTextBoxParser';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(FragmentedTextBoxParser);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7}],19:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _SegmentsUtilsJs = _dereq_('./SegmentsUtils.js');

function ListSegmentsGetter(config, isDynamic) {

    var timelineConverter = config.timelineConverter;

    var instance = undefined;

    function getSegmentsFromList(representation, requestedTime, index, availabilityUpperLimit) {
        var list = representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].SegmentList;
        var baseURL = representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].BaseURL;
        var len = list.SegmentURL_asArray.length;

        var segments = [];

        var periodSegIdx, seg, s, range, startIdx, endIdx, start;

        start = representation.startNumber;

        range = (0, _SegmentsUtilsJs.decideSegmentListRangeForTemplate)(timelineConverter, isDynamic, representation, requestedTime, index, availabilityUpperLimit);
        startIdx = Math.max(range.start, 0);
        endIdx = Math.min(range.end, list.SegmentURL_asArray.length - 1);

        for (periodSegIdx = startIdx; periodSegIdx <= endIdx; periodSegIdx++) {
            s = list.SegmentURL_asArray[periodSegIdx];

            seg = (0, _SegmentsUtilsJs.getIndexBasedSegment)(timelineConverter, isDynamic, representation, periodSegIdx);
            seg.replacementTime = (start + periodSegIdx - 1) * representation.segmentDuration;
            seg.media = s.media ? s.media : baseURL;
            seg.mediaRange = s.mediaRange;
            seg.index = s.index;
            seg.indexRange = s.indexRange;

            segments.push(seg);
            seg = null;
        }

        representation.availableSegmentsNumber = len;

        return segments;
    }

    instance = {
        getSegments: getSegmentsFromList
    };

    return instance;
}

ListSegmentsGetter.__dashjs_factory_name = 'ListSegmentsGetter';
var factory = _coreFactoryMakerJs2['default'].getClassFactory(ListSegmentsGetter);
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7,"./SegmentsUtils.js":21}],20:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _TimelineSegmentsGetterJs = _dereq_('./TimelineSegmentsGetter.js');

var _TimelineSegmentsGetterJs2 = _interopRequireDefault(_TimelineSegmentsGetterJs);

var _TemplateSegmentsGetterJs = _dereq_('./TemplateSegmentsGetter.js');

var _TemplateSegmentsGetterJs2 = _interopRequireDefault(_TemplateSegmentsGetterJs);

var _ListSegmentsGetterJs = _dereq_('./ListSegmentsGetter.js');

var _ListSegmentsGetterJs2 = _interopRequireDefault(_ListSegmentsGetterJs);

function SegmentsGetter(config, isDynamic) {

    var context = this.context;

    var instance = undefined,
        timelineSegmentsGetter = undefined,
        templateSegmentsGetter = undefined,
        listSegmentsGetter = undefined;

    function setup() {
        timelineSegmentsGetter = (0, _TimelineSegmentsGetterJs2['default'])(context).create(config, isDynamic);
        templateSegmentsGetter = (0, _TemplateSegmentsGetterJs2['default'])(context).create(config, isDynamic);
        listSegmentsGetter = (0, _ListSegmentsGetterJs2['default'])(context).create(config, isDynamic);
    }

    function getSegments(representation, requestedTime, index, onSegmentListUpdatedCallback, availabilityUpperLimit) {
        var segments;
        var type = representation.segmentInfoType;

        // Already figure out the segments.
        if (type === 'SegmentBase' || type === 'BaseURL' || !isSegmentListUpdateRequired(representation, index)) {
            segments = representation.segments;
        } else {
            if (type === 'SegmentTimeline') {
                segments = timelineSegmentsGetter.getSegments(representation, requestedTime, index, availabilityUpperLimit);
            } else if (type === 'SegmentTemplate') {
                segments = templateSegmentsGetter.getSegments(representation, requestedTime, index, availabilityUpperLimit);
            } else if (type === 'SegmentList') {
                segments = listSegmentsGetter.getSegments(representation, requestedTime, index, availabilityUpperLimit);
            }

            if (onSegmentListUpdatedCallback) {
                onSegmentListUpdatedCallback(representation, segments);
            }
        }

        return segments;
    }

    function isSegmentListUpdateRequired(representation, index) {
        var segments = representation.segments;
        var updateRequired = false;

        var upperIdx, lowerIdx;

        if (!segments || segments.length === 0) {
            updateRequired = true;
        } else {
            lowerIdx = segments[0].availabilityIdx;
            upperIdx = segments[segments.length - 1].availabilityIdx;
            updateRequired = index < lowerIdx || index > upperIdx;
        }

        return updateRequired;
    }

    instance = {
        getSegments: getSegments
    };

    setup();

    return instance;
}

SegmentsGetter.__dashjs_factory_name = 'SegmentsGetter';
var factory = _coreFactoryMakerJs2['default'].getClassFactory(SegmentsGetter);
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7,"./ListSegmentsGetter.js":19,"./TemplateSegmentsGetter.js":22,"./TimelineSegmentsGetter.js":24}],21:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});
exports.replaceTokenForTemplate = replaceTokenForTemplate;
exports.getIndexBasedSegment = getIndexBasedSegment;
exports.getTimeBasedSegment = getTimeBasedSegment;
exports.getSegmentByIndex = getSegmentByIndex;
exports.decideSegmentListRangeForTimeline = decideSegmentListRangeForTimeline;
exports.decideSegmentListRangeForTemplate = decideSegmentListRangeForTemplate;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _voSegmentJs = _dereq_('./../vo/Segment.js');

var _voSegmentJs2 = _interopRequireDefault(_voSegmentJs);

function zeroPadToLength(numStr, minStrLength) {
    while (numStr.length < minStrLength) {
        numStr = '0' + numStr;
    }
    return numStr;
}

function getNumberForSegment(segment, segmentIndex) {
    return segment.representation.startNumber + segmentIndex;
}

function replaceTokenForTemplate(url, token, value) {
    var formatTag = '%0';

    var startPos, endPos, formatTagPos, specifier, width, paddedValue;

    var tokenLen = token.length;
    var formatTagLen = formatTag.length;

    // keep looping round until all instances of <token> have been
    // replaced. once that has happened, startPos below will be -1
    // and the completed url will be returned.
    while (true) {

        // check if there is a valid $<token>...$ identifier
        // if not, return the url as is.
        startPos = url.indexOf('$' + token);
        if (startPos < 0) {
            return url;
        }

        // the next '$' must be the end of the identifier
        // if there isn't one, return the url as is.
        endPos = url.indexOf('$', startPos + tokenLen);
        if (endPos < 0) {
            return url;
        }

        // now see if there is an additional format tag suffixed to
        // the identifier within the enclosing '$' characters
        formatTagPos = url.indexOf(formatTag, startPos + tokenLen);
        if (formatTagPos > startPos && formatTagPos < endPos) {

            specifier = url.charAt(endPos - 1);
            width = parseInt(url.substring(formatTagPos + formatTagLen, endPos - 1), 10);

            // support the minimum specifiers required by IEEE 1003.1
            // (d, i , o, u, x, and X) for completeness
            switch (specifier) {
                // treat all int types as uint,
                // hence deliberate fallthrough
                case 'd':
                case 'i':
                case 'u':
                    paddedValue = zeroPadToLength(value.toString(), width);
                    break;
                case 'x':
                    paddedValue = zeroPadToLength(value.toString(16), width);
                    break;
                case 'X':
                    paddedValue = zeroPadToLength(value.toString(16), width).toUpperCase();
                    break;
                case 'o':
                    paddedValue = zeroPadToLength(value.toString(8), width);
                    break;
                default:
                    //TODO: commented out logging to supress jshint warning -- `log` is undefined here
                    //log('Unsupported/invalid IEEE 1003.1 format identifier string in URL');
                    return url;
            }
        } else {
            paddedValue = value;
        }

        url = url.substring(0, startPos) + paddedValue + url.substring(endPos + 1);
    }
}

function getIndexBasedSegment(timelineConverter, isDynamic, representation, index) {
    var seg, duration, presentationStartTime, presentationEndTime;

    duration = representation.segmentDuration;

    /*
     * From spec - If neither @duration attribute nor SegmentTimeline element is present, then the Representation
     * shall contain exactly one Media Segment. The MPD start time is 0 and the MPD duration is obtained
     * in the same way as for the last Media Segment in the Representation.
     */
    if (isNaN(duration)) {
        duration = representation.adaptation.period.duration;
    }

    presentationStartTime = representation.adaptation.period.start + index * duration;
    presentationEndTime = presentationStartTime + duration;

    seg = new _voSegmentJs2['default']();

    seg.representation = representation;
    seg.duration = duration;
    seg.presentationStartTime = presentationStartTime;

    seg.mediaStartTime = timelineConverter.calcMediaTimeFromPresentationTime(seg.presentationStartTime, representation);

    seg.availabilityStartTime = timelineConverter.calcAvailabilityStartTimeFromPresentationTime(seg.presentationStartTime, representation.adaptation.period.mpd, isDynamic);
    seg.availabilityEndTime = timelineConverter.calcAvailabilityEndTimeFromPresentationTime(presentationEndTime, representation.adaptation.period.mpd, isDynamic);

    // at this wall clock time, the video element currentTime should be seg.presentationStartTime
    seg.wallStartTime = timelineConverter.calcWallTimeForSegment(seg, isDynamic);

    seg.replacementNumber = getNumberForSegment(seg, index);
    seg.availabilityIdx = index;

    return seg;
}

function getTimeBasedSegment(timelineConverter, isDynamic, representation, time, duration, fTimescale, url, range, index) {
    var scaledTime = time / fTimescale;
    var scaledDuration = Math.min(duration / fTimescale, representation.adaptation.period.mpd.maxSegmentDuration);

    var presentationStartTime, presentationEndTime, seg;

    presentationStartTime = timelineConverter.calcPresentationTimeFromMediaTime(scaledTime, representation);
    presentationEndTime = presentationStartTime + scaledDuration;

    seg = new _voSegmentJs2['default']();

    seg.representation = representation;
    seg.duration = scaledDuration;
    seg.mediaStartTime = scaledTime;

    seg.presentationStartTime = presentationStartTime;

    // For SegmentTimeline every segment is available at loadedTime
    seg.availabilityStartTime = representation.adaptation.period.mpd.manifest.loadedTime;
    seg.availabilityEndTime = timelineConverter.calcAvailabilityEndTimeFromPresentationTime(presentationEndTime, representation.adaptation.period.mpd, isDynamic);

    // at this wall clock time, the video element currentTime should be seg.presentationStartTime
    seg.wallStartTime = timelineConverter.calcWallTimeForSegment(seg, isDynamic);

    seg.replacementTime = time;

    seg.replacementNumber = getNumberForSegment(seg, index);

    url = replaceTokenForTemplate(url, 'Number', seg.replacementNumber);
    url = replaceTokenForTemplate(url, 'Time', seg.replacementTime);
    seg.media = url;
    seg.mediaRange = range;
    seg.availabilityIdx = index;

    return seg;
}

function getSegmentByIndex(index, representation) {
    if (!representation || !representation.segments) return null;

    var ln = representation.segments.length;
    var seg, i;

    if (index < ln) {
        seg = representation.segments[index];
        if (seg && seg.availabilityIdx === index) {
            return seg;
        }
    }

    for (i = 0; i < ln; i++) {
        seg = representation.segments[i];

        if (seg && seg.availabilityIdx === index) {
            return seg;
        }
    }

    return null;
}

function decideSegmentListRangeForTimeline(timelineConverter, isDynamic, requestedTime, index, givenAvailabilityUpperLimit) {
    var availabilityLowerLimit = 2;
    var availabilityUpperLimit = givenAvailabilityUpperLimit || 10;
    var firstIdx = 0;
    var lastIdx = Number.POSITIVE_INFINITY;

    var start, end, range;

    if (isDynamic && !timelineConverter.isTimeSyncCompleted()) {
        range = { start: firstIdx, end: lastIdx };
        return range;
    }

    if (!isDynamic && requestedTime || index < 0) return null;

    // segment list should not be out of the availability window range
    start = Math.max(index - availabilityLowerLimit, firstIdx);
    end = Math.min(index + availabilityUpperLimit, lastIdx);

    range = { start: start, end: end };

    return range;
}

function decideSegmentListRangeForTemplate(timelineConverter, isDynamic, representation, requestedTime, index, givenAvailabilityUpperLimit) {
    var duration = representation.segmentDuration;
    var minBufferTime = representation.adaptation.period.mpd.manifest.minBufferTime;
    var availabilityWindow = representation.segmentAvailabilityRange;
    var periodRelativeRange = {
        start: timelineConverter.calcPeriodRelativeTimeFromMpdRelativeTime(representation, availabilityWindow.start),
        end: timelineConverter.calcPeriodRelativeTimeFromMpdRelativeTime(representation, availabilityWindow.end)
    };
    var currentSegmentList = representation.segments;
    var availabilityLowerLimit = 2 * duration;
    var availabilityUpperLimit = givenAvailabilityUpperLimit || Math.max(2 * minBufferTime, 10 * duration);

    var originAvailabilityTime = NaN;
    var originSegment = null;

    var start, end, range;

    periodRelativeRange.start = Math.max(periodRelativeRange.start, 0);

    if (isDynamic && !timelineConverter.isTimeSyncCompleted()) {
        start = Math.floor(periodRelativeRange.start / duration);
        end = Math.floor(periodRelativeRange.end / duration);
        range = { start: start, end: end };
        return range;
    }

    // if segments exist we should try to find the latest buffered time, which is the presentation time of the
    // segment for the current index
    if (currentSegmentList && currentSegmentList.length > 0) {
        originSegment = getSegmentByIndex(index, representation);
        if (originSegment) {
            originAvailabilityTime = timelineConverter.calcPeriodRelativeTimeFromMpdRelativeTime(representation, originSegment.presentationStartTime);
        } else {
            originAvailabilityTime = index > 0 ? index * duration : timelineConverter.calcPeriodRelativeTimeFromMpdRelativeTime(representation, requestedTime);
        }
    } else {
        // If no segments exist, but index > 0, it means that we switch to the other representation, so
        // we should proceed from this time.
        // Otherwise we should start from the beginning for static mpds or from the end (live edge) for dynamic mpds
        originAvailabilityTime = index > 0 ? index * duration : isDynamic ? periodRelativeRange.end : periodRelativeRange.start;
    }

    // segment list should not be out of the availability window range
    start = Math.floor(Math.max(originAvailabilityTime - availabilityLowerLimit, periodRelativeRange.start) / duration);
    end = Math.floor(Math.min(start + availabilityUpperLimit / duration, periodRelativeRange.end / duration));

    range = { start: start, end: end };

    return range;
}

},{"./../vo/Segment.js":32}],22:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _SegmentsUtilsJs = _dereq_('./SegmentsUtils.js');

function TemplateSegmentsGetter(config, isDynamic) {

    var timelineConverter = config.timelineConverter;

    var instance = undefined;

    function getSegmentsFromTemplate(representation, requestedTime, index, availabilityUpperLimit) {
        var template = representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].SegmentTemplate;
        var duration = representation.segmentDuration;
        var availabilityWindow = representation.segmentAvailabilityRange;

        var segments = [];
        var url = null;
        var seg = null;

        var segmentRange, periodSegIdx, startIdx, endIdx, start;

        start = representation.startNumber;

        if (isNaN(duration) && !isDynamic) {
            segmentRange = { start: start, end: start };
        } else {
            segmentRange = (0, _SegmentsUtilsJs.decideSegmentListRangeForTemplate)(timelineConverter, isDynamic, representation, requestedTime, index, availabilityUpperLimit);
        }

        startIdx = segmentRange.start;
        endIdx = segmentRange.end;

        for (periodSegIdx = startIdx; periodSegIdx <= endIdx; periodSegIdx++) {

            seg = (0, _SegmentsUtilsJs.getIndexBasedSegment)(timelineConverter, isDynamic, representation, periodSegIdx);
            seg.replacementTime = (start + periodSegIdx - 1) * representation.segmentDuration;
            url = template.media;
            url = (0, _SegmentsUtilsJs.replaceTokenForTemplate)(url, 'Number', seg.replacementNumber);
            url = (0, _SegmentsUtilsJs.replaceTokenForTemplate)(url, 'Time', seg.replacementTime);
            seg.media = url;

            segments.push(seg);
            seg = null;
        }

        if (isNaN(duration)) {
            representation.availableSegmentsNumber = 1;
        } else {
            representation.availableSegmentsNumber = Math.ceil((availabilityWindow.end - availabilityWindow.start) / duration);
        }

        return segments;
    }

    instance = {
        getSegments: getSegmentsFromTemplate
    };

    return instance;
}

TemplateSegmentsGetter.__dashjs_factory_name = 'TemplateSegmentsGetter';
var factory = _coreFactoryMakerJs2['default'].getClassFactory(TemplateSegmentsGetter);
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7,"./SegmentsUtils.js":21}],23:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function TimelineConverter() {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        clientServerTimeShift = undefined,
        isClientServerTimeSyncCompleted = undefined,
        expectedLiveEdge = undefined;

    function initialize() {

        clientServerTimeShift = 0;
        isClientServerTimeSyncCompleted = false;
        expectedLiveEdge = NaN;

        eventBus.on(_coreEventsEventsJs2['default'].LIVE_EDGE_SEARCH_COMPLETED, onLiveEdgeSearchCompleted, this);
        eventBus.on(_coreEventsEventsJs2['default'].TIME_SYNCHRONIZATION_COMPLETED, onTimeSyncComplete, this);
    }

    function isTimeSyncCompleted() {
        return isClientServerTimeSyncCompleted;
    }

    function setTimeSyncCompleted(value) {
        isClientServerTimeSyncCompleted = value;
    }

    function getClientTimeOffset() {
        return clientServerTimeShift;
    }

    function getExpectedLiveEdge() {
        return expectedLiveEdge;
    }

    function setExpectedLiveEdge(value) {
        expectedLiveEdge = value;
    }

    function calcAvailabilityTimeFromPresentationTime(presentationTime, mpd, isDynamic, calculateEnd) {
        var availabilityTime = NaN;

        if (calculateEnd) {
            //@timeShiftBufferDepth specifies the duration of the time shifting buffer that is guaranteed
            // to be available for a Media Presentation with type 'dynamic'.
            // When not present, the value is infinite.
            if (isDynamic && mpd.timeShiftBufferDepth != Number.POSITIVE_INFINITY) {
                availabilityTime = new Date(mpd.availabilityStartTime.getTime() + (presentationTime + mpd.timeShiftBufferDepth) * 1000);
            } else {
                availabilityTime = mpd.availabilityEndTime;
            }
        } else {
            if (isDynamic) {
                availabilityTime = new Date(mpd.availabilityStartTime.getTime() + (presentationTime - clientServerTimeShift) * 1000);
            } else {
                // in static mpd, all segments are available at the same time
                availabilityTime = mpd.availabilityStartTime;
            }
        }

        return availabilityTime;
    }

    function calcAvailabilityStartTimeFromPresentationTime(presentationTime, mpd, isDynamic) {
        return calcAvailabilityTimeFromPresentationTime.call(this, presentationTime, mpd, isDynamic);
    }

    function calcAvailabilityEndTimeFromPresentationTime(presentationTime, mpd, isDynamic) {
        return calcAvailabilityTimeFromPresentationTime.call(this, presentationTime, mpd, isDynamic, true);
    }

    function calcPresentationTimeFromWallTime(wallTime, period) {
        return (wallTime.getTime() - period.mpd.availabilityStartTime.getTime() + clientServerTimeShift * 1000) / 1000;
    }

    function calcPresentationTimeFromMediaTime(mediaTime, representation) {
        var periodStart = representation.adaptation.period.start;
        var presentationOffset = representation.presentationTimeOffset;

        return mediaTime + (periodStart - presentationOffset);
    }

    function calcMediaTimeFromPresentationTime(presentationTime, representation) {
        var periodStart = representation.adaptation.period.start;
        var presentationOffset = representation.presentationTimeOffset;

        return presentationTime - periodStart + presentationOffset;
    }

    function calcWallTimeForSegment(segment, isDynamic) {
        var suggestedPresentationDelay, displayStartTime, wallTime;

        if (isDynamic) {
            suggestedPresentationDelay = segment.representation.adaptation.period.mpd.suggestedPresentationDelay;
            displayStartTime = segment.presentationStartTime + suggestedPresentationDelay;
            wallTime = new Date(segment.availabilityStartTime.getTime() + displayStartTime * 1000);
        }

        return wallTime;
    }

    function calcSegmentAvailabilityRange(representation, isDynamic) {
        var start = representation.adaptation.period.start;
        var end = start + representation.adaptation.period.duration;
        var range = { start: start, end: end };
        var d = representation.segmentDuration || (representation.segments && representation.segments.length ? representation.segments[representation.segments.length - 1].duration : 0);

        var checkTime, now;

        if (!isDynamic) return range;

        if (!isClientServerTimeSyncCompleted && representation.segmentAvailabilityRange) {
            return representation.segmentAvailabilityRange;
        }

        checkTime = representation.adaptation.period.mpd.checkTime;
        now = calcPresentationTimeFromWallTime(new Date(), representation.adaptation.period);
        //the Media Segment list is further restricted by the CheckTime together with the MPD attribute
        // MPD@timeShiftBufferDepth such that only Media Segments for which the sum of the start time of the
        // Media Segment and the Period start time falls in the interval [NOW- MPD@timeShiftBufferDepth - @duration, min(CheckTime, NOW)] are included.
        start = Math.max(now - representation.adaptation.period.mpd.timeShiftBufferDepth, representation.adaptation.period.start);
        var timeAnchor = isNaN(checkTime) ? now : Math.min(checkTime, now);
        var periodEnd = representation.adaptation.period.start + representation.adaptation.period.duration;
        end = (timeAnchor >= periodEnd && timeAnchor - d < periodEnd ? periodEnd : timeAnchor) - d;
        //end = (isNaN(checkTime) ? now : Math.min(checkTime, now)) - d;
        range = { start: start, end: end };

        return range;
    }

    function calcPeriodRelativeTimeFromMpdRelativeTime(representation, mpdRelativeTime) {
        var periodStartTime = representation.adaptation.period.start;
        return mpdRelativeTime - periodStartTime;
    }

    function calcMpdRelativeTimeFromPeriodRelativeTime(representation, periodRelativeTime) {
        var periodStartTime = representation.adaptation.period.start;

        return periodRelativeTime + periodStartTime;
    }

    function onLiveEdgeSearchCompleted(e) {
        if (isClientServerTimeSyncCompleted || e.error) return;

        // the difference between expected and actual live edge time is supposed to be a difference between client
        // and server time as well
        clientServerTimeShift += e.liveEdge - (expectedLiveEdge + e.searchTime);
        isClientServerTimeSyncCompleted = true;
    }

    function onTimeSyncComplete(e) {
        if (isClientServerTimeSyncCompleted || e.error) {
            return;
        }

        clientServerTimeShift = e.offset / 1000;

        isClientServerTimeSyncCompleted = true;
    }

    function calcMSETimeOffset(representation) {
        // The MSEOffset is offset from AST for media. It is Period@start - presentationTimeOffset
        var presentationOffset = representation.presentationTimeOffset;
        var periodStart = representation.adaptation.period.start;
        return periodStart - presentationOffset;
    }

    function reset() {
        eventBus.off(_coreEventsEventsJs2['default'].LIVE_EDGE_SEARCH_COMPLETED, onLiveEdgeSearchCompleted, this);
        eventBus.off(_coreEventsEventsJs2['default'].TIME_SYNCHRONIZATION_COMPLETED, onTimeSyncComplete, this);
        clientServerTimeShift = 0;
        isClientServerTimeSyncCompleted = false;
        expectedLiveEdge = NaN;
    }

    instance = {
        initialize: initialize,
        isTimeSyncCompleted: isTimeSyncCompleted,
        setTimeSyncCompleted: setTimeSyncCompleted,
        getClientTimeOffset: getClientTimeOffset,
        getExpectedLiveEdge: getExpectedLiveEdge,
        setExpectedLiveEdge: setExpectedLiveEdge,
        calcAvailabilityStartTimeFromPresentationTime: calcAvailabilityStartTimeFromPresentationTime,
        calcAvailabilityEndTimeFromPresentationTime: calcAvailabilityEndTimeFromPresentationTime,
        calcPresentationTimeFromWallTime: calcPresentationTimeFromWallTime,
        calcPresentationTimeFromMediaTime: calcPresentationTimeFromMediaTime,
        calcPeriodRelativeTimeFromMpdRelativeTime: calcPeriodRelativeTimeFromMpdRelativeTime,
        calcMpdRelativeTimeFromPeriodRelativeTime: calcMpdRelativeTimeFromPeriodRelativeTime,
        calcMediaTimeFromPresentationTime: calcMediaTimeFromPresentationTime,
        calcSegmentAvailabilityRange: calcSegmentAvailabilityRange,
        calcWallTimeForSegment: calcWallTimeForSegment,
        calcMSETimeOffset: calcMSETimeOffset,
        reset: reset
    };

    return instance;
}

TimelineConverter.__dashjs_factory_name = 'TimelineConverter';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(TimelineConverter);
module.exports = exports['default'];

},{"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9}],24:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _SegmentsUtilsJs = _dereq_('./SegmentsUtils.js');

function TimelineSegmentsGetter(config, isDynamic) {

    var timelineConverter = config.timelineConverter;

    var instance = undefined;

    function getSegmentsFromTimeline(representation, requestedTime, index, availabilityUpperLimit) {
        var template = representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].SegmentTemplate;
        var timeline = template.SegmentTimeline;
        var isAvailableSegmentNumberCalculated = representation.availableSegmentsNumber > 0;

        var maxSegmentsAhead = 10;
        var time = 0;
        var scaledTime = 0;
        var availabilityIdx = -1;
        var segments = [];
        var isStartSegmentForRequestedTimeFound = false;

        var fragments, frag, i, len, j, repeat, repeatEndTime, nextFrag, calculatedRange, hasEnoughSegments, requiredMediaTime, startIdx, endIdx, fTimescale;

        var createSegment = function createSegment(s) {
            return (0, _SegmentsUtilsJs.getTimeBasedSegment)(timelineConverter, isDynamic, representation, time, s.d, fTimescale, template.media, s.mediaRange, availabilityIdx);
        };

        fTimescale = representation.timescale;

        fragments = timeline.S_asArray;

        calculatedRange = (0, _SegmentsUtilsJs.decideSegmentListRangeForTimeline)(timelineConverter, isDynamic, requestedTime, index, availabilityUpperLimit);

        // if calculatedRange exists we should generate segments that belong to this range.
        // Otherwise generate maxSegmentsAhead segments ahead of the requested time
        if (calculatedRange) {
            startIdx = calculatedRange.start;
            endIdx = calculatedRange.end;
        } else {
            requiredMediaTime = timelineConverter.calcMediaTimeFromPresentationTime(requestedTime || 0, representation);
        }

        for (i = 0, len = fragments.length; i < len; i++) {
            frag = fragments[i];
            repeat = 0;
            if (frag.hasOwnProperty('r')) {
                repeat = frag.r;
            }

            //For a repeated S element, t belongs only to the first segment
            if (frag.hasOwnProperty('t')) {
                time = frag.t;
                scaledTime = time / fTimescale;
            }

            //This is a special case: "A negative value of the @r attribute of the S element indicates that the duration indicated in @d attribute repeats until the start of the next S element, the end of the Period or until the
            // next MPD update."
            if (repeat < 0) {
                nextFrag = fragments[i + 1];

                if (nextFrag && nextFrag.hasOwnProperty('t')) {
                    repeatEndTime = nextFrag.t / fTimescale;
                } else {
                    var availabilityEnd = representation.segmentAvailabilityRange ? representation.segmentAvailabilityRange.end : timelineConverter.calcSegmentAvailabilityRange(representation, isDynamic).end;
                    repeatEndTime = timelineConverter.calcMediaTimeFromPresentationTime(availabilityEnd, representation);
                    representation.segmentDuration = frag.d / fTimescale;
                }

                repeat = Math.ceil((repeatEndTime - scaledTime) / (frag.d / fTimescale)) - 1;
            }

            // if we have enough segments in the list, but we have not calculated the total number of the segments yet we
            // should continue the loop and calc the number. Once it is calculated, we can break the loop.
            if (hasEnoughSegments) {
                if (isAvailableSegmentNumberCalculated) break;
                availabilityIdx += repeat + 1;
                continue;
            }

            for (j = 0; j <= repeat; j++) {
                availabilityIdx++;

                if (calculatedRange) {
                    if (availabilityIdx > endIdx) {
                        hasEnoughSegments = true;
                        if (isAvailableSegmentNumberCalculated) break;
                        continue;
                    }

                    if (availabilityIdx >= startIdx) {
                        segments.push(createSegment(frag));
                    }
                } else {
                    if (segments.length > maxSegmentsAhead) {
                        hasEnoughSegments = true;
                        if (isAvailableSegmentNumberCalculated) break;
                        continue;
                    }

                    // In some cases when requiredMediaTime = actual end time of the last segment
                    // it is possible that this time a bit exceeds the declared end time of the last segment.
                    // in this case we still need to include the last segment in the segment list. to do this we
                    // use a correction factor = 1.5. This number is used because the largest possible deviation is
                    // is 50% of segment duration.
                    if (isStartSegmentForRequestedTimeFound) {
                        segments.push(createSegment(frag));
                    } else if (scaledTime >= requiredMediaTime - frag.d / fTimescale * 1.5) {
                        isStartSegmentForRequestedTimeFound = true;
                        segments.push(createSegment(frag));
                    }
                }

                time += frag.d;
                scaledTime = time / fTimescale;
            }
        }

        if (!isAvailableSegmentNumberCalculated) {
            representation.availableSegmentsNumber = availabilityIdx + 1;
        }

        return segments;
    }

    instance = {
        getSegments: getSegmentsFromTimeline
    };

    return instance;
}

TimelineSegmentsGetter.__dashjs_factory_name = 'TimelineSegmentsGetter';
var factory = _coreFactoryMakerJs2['default'].getClassFactory(TimelineSegmentsGetter);
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7,"./SegmentsUtils.js":21}],25:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var AdaptationSet = function AdaptationSet() {
  _classCallCheck(this, AdaptationSet);

  this.period = null;
  this.index = -1;
  this.type = null;
};

exports["default"] = AdaptationSet;
module.exports = exports["default"];

},{}],26:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var DEFAULT_DVB_PRIORITY = 1;
var DEFAULT_DVB_WEIGHT = 1;

var BaseURL = function BaseURL(url, serviceLocation, priority, weight) {
  _classCallCheck(this, BaseURL);

  this.url = url || '';
  this.serviceLocation = serviceLocation || url || '';

  // DVB extensions
  this.dvb_priority = priority || DEFAULT_DVB_PRIORITY;
  this.dvb_weight = weight || DEFAULT_DVB_WEIGHT;

  /* currently unused:
   * byteRange,
   * availabilityTimeOffset,
   * availabilityTimeComplete
   */
};

BaseURL.DEFAULT_DVB_PRIORITY = DEFAULT_DVB_PRIORITY;
BaseURL.DEFAULT_DVB_WEIGHT = DEFAULT_DVB_WEIGHT;

exports['default'] = BaseURL;
module.exports = exports['default'];

},{}],27:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var Event = function Event() {
  _classCallCheck(this, Event);

  this.duration = NaN;
  this.presentationTime = NaN;
  this.id = NaN;
  this.messageData = '';
  this.eventStream = null;
  this.presentationTimeDelta = NaN; // Specific EMSG Box parameter
};

exports['default'] = Event;
module.exports = exports['default'];

},{}],28:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var EventStream = function EventStream() {
  _classCallCheck(this, EventStream);

  this.adaptionSet = null;
  this.representation = null;
  this.period = null;
  this.timescale = 1;
  this.value = '';
  this.schemeIdUri = '';
};

exports['default'] = EventStream;
module.exports = exports['default'];

},{}],29:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Mpd = function Mpd() {
  _classCallCheck(this, Mpd);

  this.manifest = null;
  this.suggestedPresentationDelay = 0;
  this.availabilityStartTime = null;
  this.availabilityEndTime = Number.POSITIVE_INFINITY;
  this.timeShiftBufferDepth = Number.POSITIVE_INFINITY;
  this.maxSegmentDuration = Number.POSITIVE_INFINITY;
  this.checkTime = NaN;
  this.clientServerTimeShift = 0;
  this.isClientServerTimeSyncCompleted = false;
};

exports["default"] = Mpd;
module.exports = exports["default"];

},{}],30:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var Period = function Period() {
  _classCallCheck(this, Period);

  this.id = null;
  this.index = -1;
  this.duration = NaN;
  this.start = NaN;
  this.mpd = null;
};

Period.DEFAULT_ID = 'defaultId';

exports['default'] = Period;
module.exports = exports['default'];

},{}],31:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Representation = function Representation() {
  _classCallCheck(this, Representation);

  this.id = null;
  this.index = -1;
  this.adaptation = null;
  this.segmentInfoType = null;
  this.initialization = null;
  this.segmentDuration = NaN;
  this.timescale = 1;
  this.startNumber = 1;
  this.indexRange = null;
  this.range = null;
  this.presentationTimeOffset = 0;
  // Set the source buffer timeOffset to this
  this.MSETimeOffset = NaN;
  this.segmentAvailabilityRange = null;
  this.availableSegmentsNumber = 0;
  this.bandwidth = NaN;
  this.maxPlayoutRate = NaN;
};

exports["default"] = Representation;
module.exports = exports["default"];

},{}],32:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Segment = function Segment() {
  _classCallCheck(this, Segment);

  this.indexRange = null;
  this.index = null;
  this.mediaRange = null;
  this.media = null;
  this.duration = NaN;
  // this is the time that should be inserted into the media url
  this.replacementTime = null;
  // this is the number that should be inserted into the media url
  this.replacementNumber = NaN;
  // This is supposed to match the time encoded in the media Segment
  this.mediaStartTime = NaN;
  // When the source buffer timeOffset is set to MSETimeOffset this is the
  // time that will match the seekTarget and video.currentTime
  this.presentationStartTime = NaN;
  // Do not schedule this segment until
  this.availabilityStartTime = NaN;
  // Ignore and  discard this segment after
  this.availabilityEndTime = NaN;
  // The index of the segment inside the availability window
  this.availabilityIdx = NaN;
  // For dynamic mpd's, this is the wall clock time that the video
  // element currentTime should be presentationStartTime
  this.wallStartTime = NaN;
  this.representation = null;
};

exports["default"] = Segment;
module.exports = exports["default"];

},{}],33:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var UTCTiming = function UTCTiming() {
  _classCallCheck(this, UTCTiming);

  // UTCTiming is a DescriptorType and doesn't have any additional fields
  this.schemeIdUri = '';
  this.value = '';
};

exports['default'] = UTCTiming;
module.exports = exports['default'];

},{}],34:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _XHRLoaderJs = _dereq_('./XHRLoader.js');

var _XHRLoaderJs2 = _interopRequireDefault(_XHRLoaderJs);

var _voHeadRequestJs = _dereq_('./vo/HeadRequest.js');

var _voHeadRequestJs2 = _interopRequireDefault(_voHeadRequestJs);

var _voErrorJs = _dereq_('./vo/Error.js');

var _voErrorJs2 = _interopRequireDefault(_voErrorJs);

var _coreEventBusJs = _dereq_('./../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('./../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var FRAGMENT_LOADER_ERROR_LOADING_FAILURE = 1;
var FRAGMENT_LOADER_ERROR_NULL_REQUEST = 2;
var FRAGMENT_LOADER_MESSAGE_NULL_REQUEST = 'request is null';

function FragmentLoader(config) {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        xhrLoader = undefined;

    function setup() {
        xhrLoader = (0, _XHRLoaderJs2['default'])(context).create({
            errHandler: config.errHandler,
            metricsModel: config.metricsModel,
            requestModifier: config.requestModifier
        });
    }

    function checkForExistence(request) {
        var report = function report(success) {
            eventBus.trigger(_coreEventsEventsJs2['default'].CHECK_FOR_EXISTENCE_COMPLETED, {
                request: request,
                exists: success
            });
        };

        if (request) {
            var headRequest = new _voHeadRequestJs2['default'](request.url);

            xhrLoader.load({
                request: headRequest,
                success: function success() {
                    report(true);
                },
                error: function error() {
                    report(false);
                }
            });
        } else {
            report(false);
        }
    }

    function load(request) {
        var report = function report(data, error) {
            eventBus.trigger(_coreEventsEventsJs2['default'].LOADING_COMPLETED, {
                request: request,
                response: data || null,
                error: error || null,
                sender: instance
            });
        };

        if (request) {
            xhrLoader.load({
                request: request,
                progress: function progress() {
                    eventBus.trigger(_coreEventsEventsJs2['default'].LOADING_PROGRESS, {
                        request: request
                    });
                },
                success: function success(data) {
                    report(data);
                },
                error: function error(xhr, statusText, errorText) {
                    report(undefined, new _voErrorJs2['default'](FRAGMENT_LOADER_ERROR_LOADING_FAILURE, errorText, statusText));
                }
            });
        } else {
            report(undefined, new _voErrorJs2['default'](FRAGMENT_LOADER_ERROR_NULL_REQUEST, FRAGMENT_LOADER_MESSAGE_NULL_REQUEST));
        }
    }

    function abort() {
        if (xhrLoader) {
            xhrLoader.abort();
        }
    }

    function reset() {
        if (xhrLoader) {
            xhrLoader.abort();
            xhrLoader = null;
        }
    }

    instance = {
        checkForExistence: checkForExistence,
        load: load,
        abort: abort,
        reset: reset
    };

    setup();

    return instance;
}

FragmentLoader.__dashjs_factory_name = 'FragmentLoader';

var factory = _coreFactoryMakerJs2['default'].getClassFactory(FragmentLoader);
factory.FRAGMENT_LOADER_ERROR_LOADING_FAILURE = FRAGMENT_LOADER_ERROR_LOADING_FAILURE;
factory.FRAGMENT_LOADER_ERROR_NULL_REQUEST = FRAGMENT_LOADER_ERROR_NULL_REQUEST;
exports['default'] = factory;
module.exports = exports['default'];

},{"../core/FactoryMaker.js":7,"./../core/EventBus.js":6,"./../core/events/Events.js":9,"./XHRLoader.js":44,"./vo/Error.js":100,"./vo/HeadRequest.js":102}],35:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _controllersXlinkControllerJs = _dereq_('./controllers/XlinkController.js');

var _controllersXlinkControllerJs2 = _interopRequireDefault(_controllersXlinkControllerJs);

var _XHRLoaderJs = _dereq_('./XHRLoader.js');

var _XHRLoaderJs2 = _interopRequireDefault(_XHRLoaderJs);

var _utilsURLUtilsJs = _dereq_('./utils/URLUtils.js');

var _utilsURLUtilsJs2 = _interopRequireDefault(_utilsURLUtilsJs);

var _voTextRequestJs = _dereq_('./vo/TextRequest.js');

var _voTextRequestJs2 = _interopRequireDefault(_voTextRequestJs);

var _voErrorJs = _dereq_('./vo/Error.js');

var _voErrorJs2 = _interopRequireDefault(_voErrorJs);

var _voMetricsHTTPRequestJs = _dereq_('./vo/metrics/HTTPRequest.js');

var _voMetricsHTTPRequestJs2 = _interopRequireDefault(_voMetricsHTTPRequestJs);

var _coreEventBusJs = _dereq_('../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var MANIFEST_LOADER_ERROR_PARSING_FAILURE = 1;
var MANIFEST_LOADER_ERROR_LOADING_FAILURE = 2;
var MANIFEST_LOADER_MESSAGE_PARSING_FAILURE = 'parsing failed';

function ManifestLoader(config) {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();
    var urlUtils = (0, _utilsURLUtilsJs2['default'])(context).getInstance();
    var parser = config.parser;

    var instance = undefined,
        xhrLoader = undefined,
        xlinkController = undefined;

    function setup() {
        eventBus.on(_coreEventsEventsJs2['default'].XLINK_READY, onXlinkReady, instance);

        xhrLoader = (0, _XHRLoaderJs2['default'])(context).create({
            errHandler: config.errHandler,
            metricsModel: config.metricsModel,
            requestModifier: config.requestModifier
        });

        xlinkController = (0, _controllersXlinkControllerJs2['default'])(context).create({
            errHandler: config.errHandler,
            metricsModel: config.metricsModel,
            requestModifier: config.requestModifier
        });
    }

    function onXlinkReady(event) {
        eventBus.trigger(_coreEventsEventsJs2['default'].INTERNAL_MANIFEST_LOADED, {
            manifest: event.manifest
        });
    }

    function load(url) {
        var request = new _voTextRequestJs2['default'](url, _voMetricsHTTPRequestJs2['default'].MPD_TYPE);

        xhrLoader.load({
            request: request,
            success: function success(data, textStatus, xhr) {
                var actualUrl;
                var baseUri;

                // Handle redirects for the MPD - as per RFC3986 Section 5.1.3
                if (xhr.responseURL && xhr.responseURL !== url) {
                    baseUri = urlUtils.parseBaseUrl(xhr.responseURL);
                    actualUrl = xhr.responseURL;
                } else {
                    baseUri = urlUtils.parseBaseUrl(url);
                }

                var manifest = parser.parse(data, xlinkController);

                if (manifest) {
                    manifest.url = actualUrl || url;

                    // URL from which the MPD was originally retrieved (MPD updates will not change this value)
                    if (!manifest.originalUrl) {
                        manifest.originalUrl = manifest.url;
                    }

                    manifest.baseUri = baseUri;
                    manifest.loadedTime = new Date();
                    xlinkController.resolveManifestOnLoad(manifest);
                } else {
                    eventBus.trigger(_coreEventsEventsJs2['default'].INTERNAL_MANIFEST_LOADED, {
                        manifest: null,
                        error: new _voErrorJs2['default'](MANIFEST_LOADER_ERROR_PARSING_FAILURE, MANIFEST_LOADER_MESSAGE_PARSING_FAILURE)
                    });
                }
            },
            error: function error(xhr, statusText, errorText) {
                eventBus.trigger(_coreEventsEventsJs2['default'].INTERNAL_MANIFEST_LOADED, {
                    manifest: null,
                    error: new _voErrorJs2['default'](MANIFEST_LOADER_ERROR_LOADING_FAILURE, 'Failed loading manifest: ' + url + ', ' + errorText)
                });
            }
        });
    }

    function reset() {
        eventBus.off(_coreEventsEventsJs2['default'].XLINK_READY, onXlinkReady, instance);

        if (xlinkController) {
            xlinkController.reset();
            xlinkController = null;
        }

        if (xhrLoader) {
            xhrLoader.abort();
            xhrLoader = null;
        }
    }

    instance = {
        load: load,
        reset: reset
    };

    setup();

    return instance;
}

ManifestLoader.__dashjs_factory_name = 'ManifestLoader';

var factory = _coreFactoryMakerJs2['default'].getClassFactory(ManifestLoader);
factory.MANIFEST_LOADER_ERROR_PARSING_FAILURE = MANIFEST_LOADER_ERROR_PARSING_FAILURE;
factory.MANIFEST_LOADER_ERROR_LOADING_FAILURE = MANIFEST_LOADER_ERROR_LOADING_FAILURE;
exports['default'] = factory;
module.exports = exports['default'];

},{"../core/EventBus.js":6,"../core/FactoryMaker.js":7,"../core/events/Events.js":9,"./XHRLoader.js":44,"./controllers/XlinkController.js":60,"./utils/URLUtils.js":96,"./vo/Error.js":100,"./vo/TextRequest.js":108,"./vo/metrics/HTTPRequest.js":117}],36:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreEventBusJs = _dereq_('../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

function ManifestUpdater() {

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        refreshDelay = undefined,
        refreshTimer = undefined,
        isStopped = undefined,
        isUpdating = undefined,
        manifestLoader = undefined,
        manifestModel = undefined,
        dashManifestModel = undefined;

    function setConfig(config) {
        if (!config) return;

        if (config.manifestModel) {
            manifestModel = config.manifestModel;
        }
        if (config.dashManifestModel) {
            dashManifestModel = config.dashManifestModel;
        }
    }

    function initialize(loader) {
        manifestLoader = loader;
        refreshDelay = NaN;
        refreshTimer = null;
        isUpdating = false;
        isStopped = true;

        eventBus.on(_coreEventsEventsJs2['default'].STREAMS_COMPOSED, onStreamsComposed, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_STARTED, onPlaybackStarted, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_PAUSED, onPlaybackPaused, this);
        eventBus.on(_coreEventsEventsJs2['default'].INTERNAL_MANIFEST_LOADED, onManifestLoaded, this);
    }

    function setManifest(manifest) {
        update(manifest);
    }

    function getManifestLoader() {
        return manifestLoader;
    }

    function reset() {
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_STARTED, onPlaybackStarted, this);
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_PAUSED, onPlaybackPaused, this);
        eventBus.off(_coreEventsEventsJs2['default'].STREAMS_COMPOSED, onStreamsComposed, this);
        eventBus.off(_coreEventsEventsJs2['default'].INTERNAL_MANIFEST_LOADED, onManifestLoaded, this);

        isStopped = true;
        isUpdating = false;
        clear();
        refreshDelay = NaN;
    }

    function clear() {
        if (refreshTimer !== null) {
            clearInterval(refreshTimer);
            refreshTimer = null;
        }
    }

    function startManifestRefreshTimer() {
        clear();
        if (!isNaN(refreshDelay)) {
            log('Refresh manifest in ' + refreshDelay + ' seconds.');
            refreshTimer = setTimeout(onRefreshTimer, Math.min(refreshDelay * 1000, Math.pow(2, 31) - 1), this);
        }
    }

    function update(manifest) {
        var delay, timeSinceLastUpdate;

        var date = new Date();

        manifestModel.setValue(manifest);
        log('Manifest has been refreshed at ' + date + '[' + date.getTime() / 1000 + '] ');

        delay = dashManifestModel.getRefreshDelay(manifest);
        timeSinceLastUpdate = (new Date().getTime() - manifest.loadedTime.getTime()) / 1000;
        refreshDelay = Math.max(delay - timeSinceLastUpdate, 0);

        eventBus.trigger(_coreEventsEventsJs2['default'].MANIFEST_UPDATED, { manifest: manifest });

        if (!isStopped) {
            startManifestRefreshTimer();
        }
    }

    function onRefreshTimer() {
        var manifest, url;

        if (isStopped || isUpdating) return;

        isUpdating = true;
        manifest = manifestModel.getValue();
        url = manifest.url;

        if (manifest.hasOwnProperty('Location')) {
            url = manifest.Location;
        }

        //log("Refresh manifest @ " + url);

        manifestLoader.load(url);
    }

    function onManifestLoaded(e) {
        if (!e.error) {
            update(e.manifest);
        }
    }

    function onPlaybackStarted() /*e*/{
        isStopped = false;
        startManifestRefreshTimer();
    }

    function onPlaybackPaused() /*e*/{
        isStopped = true;
        clear();
    }

    function onStreamsComposed() /*e*/{
        // When streams are ready we can consider manifest update completed. Resolve the update promise.
        isUpdating = false;
    }

    instance = {
        initialize: initialize,
        setManifest: setManifest,
        getManifestLoader: getManifestLoader,
        setConfig: setConfig,
        reset: reset
    };

    return instance;
}
ManifestUpdater.__dashjs_factory_name = 'ManifestUpdater';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(ManifestUpdater);
module.exports = exports['default'];

},{"../core/Debug.js":5,"../core/EventBus.js":6,"../core/FactoryMaker.js":7,"../core/events/Events.js":9}],37:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _dashVoUTCTimingJs = _dereq_('../dash/vo/UTCTiming.js');

var _dashVoUTCTimingJs2 = _interopRequireDefault(_dashVoUTCTimingJs);

var _controllersPlaybackControllerJs = _dereq_('./controllers/PlaybackController.js');

var _controllersPlaybackControllerJs2 = _interopRequireDefault(_controllersPlaybackControllerJs);

var _controllersStreamControllerJs = _dereq_('./controllers/StreamController.js');

var _controllersStreamControllerJs2 = _interopRequireDefault(_controllersStreamControllerJs);

var _controllersMediaControllerJs = _dereq_('./controllers/MediaController.js');

var _controllersMediaControllerJs2 = _interopRequireDefault(_controllersMediaControllerJs);

var _ManifestLoaderJs = _dereq_('./ManifestLoader.js');

var _ManifestLoaderJs2 = _interopRequireDefault(_ManifestLoaderJs);

var _utilsLiveEdgeFinderJs = _dereq_('./utils/LiveEdgeFinder.js');

var _utilsLiveEdgeFinderJs2 = _interopRequireDefault(_utilsLiveEdgeFinderJs);

var _utilsErrorHandlerJs = _dereq_('./utils/ErrorHandler.js');

var _utilsErrorHandlerJs2 = _interopRequireDefault(_utilsErrorHandlerJs);

var _utilsCapabilitiesJs = _dereq_('./utils/Capabilities.js');

var _utilsCapabilitiesJs2 = _interopRequireDefault(_utilsCapabilitiesJs);

var _TextTracksJs = _dereq_('./TextTracks.js');

var _TextTracksJs2 = _interopRequireDefault(_TextTracksJs);

var _controllersSourceBufferControllerJs = _dereq_('./controllers/SourceBufferController.js');

var _controllersSourceBufferControllerJs2 = _interopRequireDefault(_controllersSourceBufferControllerJs);

var _VirtualBufferJs = _dereq_('./VirtualBuffer.js');

var _VirtualBufferJs2 = _interopRequireDefault(_VirtualBufferJs);

var _utilsRequestModifierJs = _dereq_('./utils/RequestModifier.js');

var _utilsRequestModifierJs2 = _interopRequireDefault(_utilsRequestModifierJs);

var _TextSourceBufferJs = _dereq_('./TextSourceBuffer.js');

var _TextSourceBufferJs2 = _interopRequireDefault(_TextSourceBufferJs);

var _modelsURIQueryAndFragmentModelJs = _dereq_('./models/URIQueryAndFragmentModel.js');

var _modelsURIQueryAndFragmentModelJs2 = _interopRequireDefault(_modelsURIQueryAndFragmentModelJs);

var _modelsManifestModelJs = _dereq_('./models/ManifestModel.js');

var _modelsManifestModelJs2 = _interopRequireDefault(_modelsManifestModelJs);

var _modelsMediaPlayerModelJs = _dereq_('./models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

var _modelsMetricsModelJs = _dereq_('./models/MetricsModel.js');

var _modelsMetricsModelJs2 = _interopRequireDefault(_modelsMetricsModelJs);

var _controllersAbrControllerJs = _dereq_('./controllers/AbrController.js');

var _controllersAbrControllerJs2 = _interopRequireDefault(_controllersAbrControllerJs);

var _controllersTimeSyncControllerJs = _dereq_('./controllers/TimeSyncController.js');

var _controllersTimeSyncControllerJs2 = _interopRequireDefault(_controllersTimeSyncControllerJs);

var _rulesAbrABRRulesCollectionJs = _dereq_('./rules/abr/ABRRulesCollection.js');

var _rulesAbrABRRulesCollectionJs2 = _interopRequireDefault(_rulesAbrABRRulesCollectionJs);

var _modelsVideoModelJs = _dereq_('./models/VideoModel.js');

var _modelsVideoModelJs2 = _interopRequireDefault(_modelsVideoModelJs);

var _rulesRulesControllerJs = _dereq_('./rules/RulesController.js');

var _rulesRulesControllerJs2 = _interopRequireDefault(_rulesRulesControllerJs);

var _rulesSynchronizationSynchronizationRulesCollectionJs = _dereq_('./rules/synchronization/SynchronizationRulesCollection.js');

var _rulesSynchronizationSynchronizationRulesCollectionJs2 = _interopRequireDefault(_rulesSynchronizationSynchronizationRulesCollectionJs);

var _controllersMediaSourceControllerJs = _dereq_('./controllers/MediaSourceController.js');

var _controllersMediaSourceControllerJs2 = _interopRequireDefault(_controllersMediaSourceControllerJs);

var _controllersBaseURLControllerJs = _dereq_('./controllers/BaseURLController.js');

var _controllersBaseURLControllerJs2 = _interopRequireDefault(_controllersBaseURLControllerJs);

var _coreDebugJs = _dereq_('./../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var _coreEventBusJs = _dereq_('./../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('./../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _MediaPlayerEventsJs = _dereq_('./MediaPlayerEvents.js');

var _MediaPlayerEventsJs2 = _interopRequireDefault(_MediaPlayerEventsJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

//Dash

var _dashDashAdapterJs = _dereq_('../dash/DashAdapter.js');

var _dashDashAdapterJs2 = _interopRequireDefault(_dashDashAdapterJs);

var _dashDashParserJs = _dereq_('../dash/DashParser.js');

var _dashDashParserJs2 = _interopRequireDefault(_dashDashParserJs);

var _dashModelsDashManifestModelJs = _dereq_('../dash/models/DashManifestModel.js');

var _dashModelsDashManifestModelJs2 = _interopRequireDefault(_dashModelsDashManifestModelJs);

var _dashDashMetricsJs = _dereq_('../dash/DashMetrics.js');

var _dashDashMetricsJs2 = _interopRequireDefault(_dashDashMetricsJs);

var _dashUtilsTimelineConverterJs = _dereq_('../dash/utils/TimelineConverter.js');

var _dashUtilsTimelineConverterJs2 = _interopRequireDefault(_dashUtilsTimelineConverterJs);

/**
 * @Module MediaPlayer
 * @description The MediaPlayer is the primary dash.js Module and a Facade to build your player around.
 * It will allow you access to all the important dash.js properties/methods via the public API and all the
 * events to build a robust DASH media player.
 */
function MediaPlayer() {

    var VERSION = '2.1.0-1';
    var PLAYBACK_NOT_INITIALIZED_ERROR = 'You must first call play() to init playback before calling this method';
    var ELEMENT_NOT_ATTACHED_ERROR = 'You must first call attachView() to set the video element before calling this method';
    var SOURCE_NOT_ATTACHED_ERROR = 'You must first call attachSource() with a valid source before calling this method';
    var MEDIA_PLAYER_NOT_INITIALIZED_ERROR = 'MediaPlayer not initialized!';

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();
    var debug = (0, _coreDebugJs2['default'])(context).getInstance();
    var log = debug.log;

    var instance = undefined,
        source = undefined,
        protectionData = undefined,
        mediaPlayerInitialized = undefined,
        playbackInitialized = undefined,
        autoPlay = undefined,
        abrController = undefined,
        mediaController = undefined,
        protectionController = undefined,
        metricsReportingController = undefined,
        adapter = undefined,
        metricsModel = undefined,
        mediaPlayerModel = undefined,
        errHandler = undefined,
        capabilities = undefined,
        streamController = undefined,
        rulesController = undefined,
        playbackController = undefined,
        dashMetrics = undefined,
        dashManifestModel = undefined,
        manifestModel = undefined,
        videoModel = undefined,
        textSourceBuffer = undefined;

    function setup() {
        mediaPlayerInitialized = false;
        playbackInitialized = false;
        autoPlay = true;
        protectionController = null;
        protectionData = null;
        adapter = null;
        _coreEventsEventsJs2['default'].extend(_MediaPlayerEventsJs2['default']);
        mediaPlayerModel = (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance();
        if (window.hola_cdn && window.hola_cdn.api && window.hola_cdn.api.dashjs_message) window.hola_cdn.api.dashjs_message(instance);
    }

    /**
     * Upon creating the MediaPlayer you must call initialize before you call anything else.
     * There is one exception to this rule. It is crucial to call {@link module:MediaPlayer#extend extend()}
     * with all your extensions prior to calling initialize.
     *
     * ALL arguments are optional and there are individual methods to set each argument later on.
     * The args in this method are just for convenience and should only be used for a simple player setup.
     *
     * @param {HTML5MediaElement} view - Optional arg to set the video element. {@link module:MediaPlayer#attachView attachView()}
     * @param {string} source - Optional arg to set the media source. {@link module:MediaPlayer#attachSource attachSource()}
     * @param {boolean} AutoPlay - Optional arg to set auto play. {@link module:MediaPlayer#setAutoPlay setAutoPlay()}
     * @see {@link module:MediaPlayer#attachView attachView()}
     * @see {@link module:MediaPlayer#attachSource attachSource()}
     * @see {@link module:MediaPlayer#setAutoPlay setAutoPlay()}
     * @memberof module:MediaPlayer
     * @instance
     */
    function initialize(view, source, AutoPlay) {

        capabilities = (0, _utilsCapabilitiesJs2['default'])(context).getInstance();
        errHandler = (0, _utilsErrorHandlerJs2['default'])(context).getInstance();

        if (!capabilities.supportsMediaSource()) {
            errHandler.capabilityError('mediasource');
            return;
        }

        if (mediaPlayerInitialized) return;
        mediaPlayerInitialized = true;

        abrController = (0, _controllersAbrControllerJs2['default'])(context).getInstance();

        playbackController = (0, _controllersPlaybackControllerJs2['default'])(context).getInstance();
        mediaController = (0, _controllersMediaControllerJs2['default'])(context).getInstance();
        mediaController.initialize();
        manifestModel = (0, _modelsManifestModelJs2['default'])(context).getInstance();
        dashManifestModel = (0, _dashModelsDashManifestModelJs2['default'])(context).getInstance();
        dashMetrics = (0, _dashDashMetricsJs2['default'])(context).getInstance();
        metricsModel = (0, _modelsMetricsModelJs2['default'])(context).getInstance();
        metricsModel.setConfig({ adapter: createAdaptor() });

        restoreDefaultUTCTimingSources();
        setAutoPlay(AutoPlay !== undefined ? AutoPlay : true);

        if (view) {
            attachView(view);
        }

        if (source) {
            attachSource(source);
        }

        log('[dash.js ' + VERSION + '] ' + 'MediaPlayer has been initialized');
    }

    /**
     * The ready state of the MediaPlayer based on both the video element and MPD source being defined.
     *
     * @returns {boolean} The current ready state of the MediaPlayer
     * @see {@link module:MediaPlayer#attachView attachView()}
     * @see {@link module:MediaPlayer#attachSource attachSource()}
     * @memberof module:MediaPlayer
     * @instance
     */
    function isReady() {
        return !!videoModel && !!source;
    }

    /**
     * The play method initiates playback of the media defined by the {@link module:MediaPlayer#attachSource attachSource()} method.
     * This method will call play on the native Video Element.
     *
     * @see {@link module:MediaPlayer#attachSource attachSource()}
     * @memberof module:MediaPlayer
     * @instance
     */
    function play() {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        if (!autoPlay || isPaused() && playbackInitialized) {
            playbackController.play();
        }
    }

    /**
     * This method will call pause on the native Video Element.
     *
     * @memberof module:MediaPlayer
     * @instance
     */
    function pause() {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        playbackController.pause();
    }

    /**
     * Returns a Boolean that indicates whether the Video Element is paused.
     * @return {boolean}
     * @memberof module:MediaPlayer
     * @instance
     */
    function isPaused() {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        return playbackController.isPaused();
    }

    /**
     * Returns a Boolean that indicates whether the media is in the process of seeking to a new position.
     * @return {boolean}
     * @memberof module:MediaPlayer
     * @instance
     */
    function isSeeking() {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        return playbackController.isSeeking();
    }

    /**
     * Use this method to set the native Video Element's muted state. Takes a Boolean that determines whether audio is muted. true if the audio is muted and false otherwise.
     * @param {boolean} value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setMute(value) {
        if (!videoModel) {
            throw ELEMENT_NOT_ATTACHED_ERROR;
        }
        getVideoElement().muted = value;
    }

    /**
     * A Boolean that determines whether audio is muted.
     * @returns {boolean}
     * @memberof module:MediaPlayer
     * @instance
     */
    function isMuted() {
        if (!videoModel) {
            throw ELEMENT_NOT_ATTACHED_ERROR;
        }
        return getVideoElement().muted;
    }

    /**
     * A double indicating the audio volume, from 0.0 (silent) to 1.0 (loudest).
     * @param {number} value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setVolume(value) {
        if (!videoModel) {
            throw ELEMENT_NOT_ATTACHED_ERROR;
        }
        getVideoElement().volume = value;
    }

    /**
     * Returns the current audio volume, from 0.0 (silent) to 1.0 (loudest).
     * @returns {number}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getVolume() {
        if (!videoModel) {
            throw ELEMENT_NOT_ATTACHED_ERROR;
        }
        return getVideoElement().volume;
    }

    /**
     * The length of the buffer for a given media type, in seconds. Valid media types are "video", "audio" and "fragmentedText". If no type
     * is passed in, then the minimum of the video and the audio buffer length is returned. The value is returned to a precision of two decimal places.
     * NaN is returned if an invalid type is requested, or if the presentation does not contain that type or if no arguments are passed and the
     * presentation doers not include any audio or video adaption sets.
     *
     * @returns {number} The length of the buffer for the given media type, in seconds.
     * @memberof module:MediaPlayer
     * @instance
     */
    function getBufferLength(type) {

        if (!type) {
            var videoBuffer = getTracksFor('video').length > 0 ? getDashMetrics().getCurrentBufferLevel(getMetricsFor('video')) : Number.MAX_SAFE_INTEGER;
            var audioBuffer = getTracksFor('audio').length > 0 ? getDashMetrics().getCurrentBufferLevel(getMetricsFor('audio')) : Number.MAX_SAFE_INTEGER;
            var textBuffer = getTracksFor('fragmentedText').length > 0 ? getDashMetrics().getCurrentBufferLevel(getMetricsFor('fragmentedText')) : Number.MAX_SAFE_INTEGER;
            return Math.min(videoBuffer, audioBuffer, textBuffer).toPrecision(3);
        } else {
            if (type === 'video' || type === 'audio' || type === 'fragmentedText') {
                var buffer = getDashMetrics().getCurrentBufferLevel(getMetricsFor(type));
                return buffer ? buffer.toPrecision(3) : NaN;
            } else {
                log('Warning  - getBufferLength requested for invalid type');
                return NaN;
            }
        }
    }

    /**
     * The timeShiftBufferLength (DVR Window), in seconds.
     *
     * @returns {number} The window of allowable play time behind the live point of a live stream.
     * @memberof module:MediaPlayer
     * @instance
     */
    function getDVRWindowSize() {
        var metric = getDVRInfoMetric();
        if (!metric) {
            return 0;
        }
        return metric.manifestInfo.DVRWindowSize;
    }

    /**
     * This method should only be used with a live stream that has a valid timeShiftBufferLength (DVR Window).
     * NOTE - If you do not need the raw offset value (i.e. media analytics, tracking, etc) consider using the {@link module:MediaPlayer#seek seek()} method
     * which will calculate this value for you and set the video element's currentTime property all in one simple call.
     *
     * @param value {number} A relative time, in seconds, based on the return value of the {@link module:MediaPlayer#duration duration()} method is expected.
     * @returns {number} A value that is relative the available range within the timeShiftBufferLength (DVR Window).
     * @see {@link module:MediaPlayer#seek seek()}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getDVRSeekOffset(value) {
        var metric = getDVRInfoMetric();

        if (!metric) {
            return 0;
        }

        var val = metric.range.start + value;

        if (val > metric.range.end) {
            val = metric.range.end;
        }

        return val;
    }

    /**
     * Sets the currentTime property of the attached video element.  If it is a live stream with a
     * timeShiftBufferLength, then the DVR window offset will be automatically calculated.
     *
     * @param value {number} A relative time, in seconds, based on the return value of the {@link module:MediaPlayer#duration duration()} method is expected
     * @see {@link module:MediaPlayer#getDVRSeekOffset getDVRSeekOffset()}
     * @memberof module:MediaPlayer
     * @instance
     */
    function seek(value) {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        var s = playbackController.getIsDynamic() ? getDVRSeekOffset(value) : value;
        playbackController.seek(s);
    }

    /**
     * Current time of the playhead, in seconds.
     *
     * If called with no arguments then the returned time value is time elapsed since the start point of the first stream.
     * However if a stream ID is supplied then time is relative to the start of that stream, or is null if there is no such stream id in the manifest.
     *
     * @param streamId The ID of a stream that the returned playhead time must be relative to the start of. If undefined, then playhead time is relative to the first stream.
     * @returns {number} The current playhead time of the media, or null.
     * @memberof module:MediaPlayer
     * @instance
     */
    function time(streamId) {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        var t = getVideoElement().currentTime;

        if (streamId !== undefined) {
            t = streamController.getTimeRelativeToStreamId(t, streamId);
        }

        if (playbackController.getIsDynamic()) {
            var metric = getDVRInfoMetric();
            t = metric === null ? 0 : duration() - (metric.range.end - metric.time);
        }
        return t;
    }

    /**
     * Duration of the media's playback, in seconds.
     *
     * @returns {number} The current duration of the media.
     * @memberof module:MediaPlayer
     * @instance
     */
    function duration() {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        var d = getVideoElement().duration;

        if (playbackController.getIsDynamic()) {

            var metric = getDVRInfoMetric();
            var range;

            if (!metric) {
                return 0;
            }

            range = metric.range.end - metric.range.start;
            d = range < metric.manifestInfo.DVRWindowSize ? range : metric.manifestInfo.DVRWindowSize;
        }
        return d;
    }

    /**
     * Use this method to get the current playhead time as an absolute value, the time in seconds since midnight UTC, Jan 1 1970.
     * Note - this property only has meaning for live streams. If called before play() has begun, it will return a value of NaN.
     *
     * @returns {number} The current playhead time as UTC timestamp.
     * @memberof module:MediaPlayer
     * @instance
     */
    function timeAsUTC() {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        if (time() < 0) {
            return NaN;
        }
        return getAsUTC(time());
    }

    /**
     * Use this method to get the current duration as an absolute value, the time in seconds since midnight UTC, Jan 1 1970.
     * Note - this property only has meaning for live streams.
     *
     * @returns {number} The current duration as UTC timestamp.
     * @memberof module:MediaPlayer
     * @instance
     */
    function durationAsUTC() {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        return getAsUTC(duration());
    }

    /**
     * A utility methods which converts UTC timestamp value into a valid time and date string.
     *
     * @param {number} time - UTC timestamp to be converted into date and time.
     * @param {string} locales - a region identifier (i.e. en_US).
     * @param {boolean} hour12 - 12 vs 24 hour. Set to true for 12 hour time formatting.
     * @returns {string} A formatted time and date string.
     * @memberof module:MediaPlayer
     * @instance
     */
    function formatUTC(time, locales, hour12) {
        var dt = new Date(time * 1000);
        var d = dt.toLocaleDateString(locales);
        var t = dt.toLocaleTimeString(locales, { hour12: hour12 });
        return t + ' ' + d;
    }

    /**
     * A utility method which converts seconds into TimeCode (i.e. 300 --> 05:00).
     *
     * @param value {number} A number in seconds to be converted into a formatted time code.
     * @returns {string} A formatted time code string.
     * @memberof module:MediaPlayer
     * @instance
     */
    function convertToTimeCode(value) {
        value = Math.max(value, 0);

        var h = Math.floor(value / 3600);
        var m = Math.floor(value % 3600 / 60);
        var s = Math.floor(value % 3600 % 60);
        return (h === 0 ? '' : h < 10 ? '0' + h.toString() + ':' : h.toString() + ':') + (m < 10 ? '0' + m.toString() : m.toString()) + ':' + (s < 10 ? '0' + s.toString() : s.toString());
    }

    /**
     * This method should be used to extend or replace internal dash.js objects.
     * There are two ways to extend dash.js (determined by the override argument):
     * <ol>
     * <li>If you set override to true any public method or property in your custom object will
     * override the dash.js parent object's property(ies) and will be used instead but the
     * dashj.s parent module will still be created.</li>
     *
     * <li>If you set override to false your object will completely replace the dash.js object.
     * (Note: This is how it was in 1.x of Dash.js with Dijon).</li>
     * </ol>
     * <b>When you extend you get access to this.context, this.factory and this.parent to operate with in your custom object.</b>
     * <ul>
     * <li><b>this.context</b> - can be used to pass context for singleton access.</li>
     * <li><b>this.factory</b> - can be used to call factory.getSingletonInstance().</li>
     * <li><b>this.parent</b> - is the reference of the parent object to call other public methods. (this.parent is excluded if you extend with override set to false or option 2)</li>
     * </ul>
     * <b>You must call extend before you call initialize</b>
     * @see {@link module:MediaPlayer#initialize initialize()}
     * @memberof module:MediaPlayer
     * @instance
     */
    function extend(parentNameString, childInstance, override) {
        _coreFactoryMakerJs2['default'].extend(parentNameString, childInstance, override, context);
    }

    /**
     * Use the on method to listen for public events found in MediaPlayer.events. {@link MediaPlayerEvents}
     *
     * @param {String} type - {@link MediaPlayerEvents}
     * @param {Function} listener - callback method when the event fires.
     * @param {Object} scope - context of the listener so it can be removed properly.
     * @memberof module:MediaPlayer
     * @instance
     */
    function on(type, listener, scope) {
        eventBus.on(type, listener, scope);
    }

    /**
     * Use the off method to remove listeners for public events found in MediaPlayer.events. {@link MediaPlayerEvents}
     *
     * @param {String} type - {@link MediaPlayerEvents}
     * @param {Function} listener - callback method when the event fires.
     * @param {Object} scope - context of the listener so it can be removed properly.
     * @memberof module:MediaPlayer
     * @instance
     */
    function off(type, listener, scope) {
        eventBus.off(type, listener, scope);
    }

    /**
     * Current version of Dash.js
     * @returns {string} the current dash.js version string.
     * @memberof module:MediaPlayer
     * @instance
     */
    function getVersion() {
        return VERSION;
    }

    /**
     * Use this method to access the dash.js logging class.
     *
     * @returns {@link module:Debug}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getDebug() {
        return debug;
    }

    /**
     * @deprecated Since version 2.1.0.  <b>Instead use:</b>
     * <ul>
     * <li>{@link module:MediaPlayer#getVideoElement getVideoElement()}</li>
     * <li>{@link module:MediaPlayer#getSource getSource()}</li>
     * <li>{@link module:MediaPlayer#getVideoContainer getVideoContainer()}</li>
     * <li>{@link module:MediaPlayer#getTTMLRenderingDiv getTTMLRenderingDiv()}</li>
     * </ul>
     *
     * @returns {@link VideoModel}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getVideoModel() {
        if (!videoModel) {
            throw ELEMENT_NOT_ATTACHED_ERROR;
        }
        return videoModel;
    }

    /**
     * <p>Changing this value will lower or increase live stream latency.  The detected segment duration will be multiplied by this value
     * to define a time in seconds to delay a live stream from the live edge.</p>
     * <p>Lowering this value will lower latency but may decrease the player's ability to build a stable buffer.</p>
     *
     * @param value {int} Represents how many segment durations to delay the live stream.
     * @default 4
     * @memberof module:MediaPlayer
     * @see {@link module:MediaPlayer#useSuggestedPresentationDelay useSuggestedPresentationDelay()}
     * @instance
     */
    function setLiveDelayFragmentCount(value) {
        mediaPlayerModel.setLiveDelayFragmentCount(value);
    }

    /**
     * <p>Equivalent in seconds of setLiveDelayFragmentCount</p>
     * <p>Lowering this value will lower latency but may decrease the player's ability to build a stable buffer.</p>
     * <p>This value should be less than the manifest duration by a couple of segment durations to avoid playback issues</p>
     * <p>If set, this parameter will take precedence over setLiveDelayFragmentCount and manifest info</p>
     *
     * @param value {int} Represents how many seconds to delay the live stream.
     * @default undefined
     * @memberof module:MediaPlayer
     * @see {@link module:MediaPlayer#useSuggestedPresentationDelay useSuggestedPresentationDelay()}
     * @instance
     */
    function setLiveDelay(value) {
        mediaPlayerModel.setLiveDelay(value);
    }

    /**
     * <p>Set to true if you would like to override the default live delay and honor the SuggestedPresentationDelay attribute in by the manifest.</p>
     * @param value {boolean}
     * @default false
     * @memberof module:MediaPlayer
     * @see {@link module:MediaPlayer#setLiveDelayFragmentCount setLiveDelayFragmentCount()}
     * @instance
     */
    function useSuggestedPresentationDelay(value) {
        mediaPlayerModel.setUseSuggestedPresentationDelay(value);
    }

    /**
     * Set to false if you would like to disable the last known bit rate from being stored during playback and used
     * to set the initial bit rate for subsequent playback within the expiration window.
     *
     * The default expiration is one hour, defined in milliseconds. If expired, the default initial bit rate (closest to 1000 kbps) will be used
     * for that session and a new bit rate will be stored during that session.
     *
     * @param enable - Boolean - Will toggle if feature is enabled. True to enable, False to disable.
     * @param ttl Number - (Optional) A value defined in milliseconds representing how long to cache the bit rate for. Time to live.
     * @default enable = True, ttl = 360000 (1 hour)
     * @memberof module:MediaPlayer
     * @instance
     *
     */
    function enableLastBitrateCaching(enable, ttl) {
        mediaPlayerModel.setLastBitrateCachingInfo(enable, ttl);
    }

    /**
     * Set to false if you would like to disable the last known lang for audio (or camera angle for video) from being stored during playback and used
     * to set the initial settings for subsequent playback within the expiration window.
     *
     * The default expiration is one hour, defined in milliseconds. If expired, the default settings will be used
     * for that session and a new settings will be stored during that session.
     *
     * @param enable - Boolean - Will toggle if feature is enabled. True to enable, False to disable.
     * @param ttl Number - (Optional) A value defined in milliseconds representing how long to cache the settings for. Time to live.
     * @default enable = True, ttl = 360000 (1 hour)
     * @memberof module:MediaPlayer
     * @instance
     *
     */
    function enableLastMediaSettingsCaching(enable, ttl) {
        mediaPlayerModel.setLastMediaSettingsCachingInfo(enable, ttl);
    }

    /**
     * When switching multi-bitrate content (auto or manual mode) this property specifies the maximum bitrate allowed.
     * If you set this property to a value lower than that currently playing, the switching engine will switch down to
     * satisfy this requirement. If you set it to a value that is lower than the lowest bitrate, it will still play
     * that lowest bitrate.
     *
     * You can set or remove this bitrate cap at anytime before or during playback.  To clear this setting you must use the API
     * and set the value param to NaN.
     *
     * This feature is typically used to reserve higher bitrates for playback only when the player is in large or full-screen format.
     *
     * @param type {String} 'video' or 'audio' are the type options.
     * @param value {int} Value in kbps representing the maximum bitrate allowed.
     * @memberof module:MediaPlayer
     * @instance
     */
    function setMaxAllowedBitrateFor(type, value) {
        abrController.setMaxAllowedBitrateFor(type, value);
    }

    /**
     * @param type {string} 'video' or 'audio' are the type options.
     * @memberof module:MediaPlayer
     * @see {@link module:MediaPlayer#setMaxAllowedBitrateFor setMaxAllowedBitrateFor()}
     * @instance
     */
    function getMaxAllowedBitrateFor(type) {
        return abrController.getMaxAllowedBitrateFor(type);
    }

    /**
     * When switching multi-bitrate content (auto or manual mode) this property specifies the maximum representation allowed,
     * as a proportion of the size of the representation set.
     *
     * You can set or remove this cap at anytime before or during playback. To clear this setting you must use the API
     * and set the value param to NaN.
     *
     * If both this and maxAllowedBitrate are defined, maxAllowedBitrate is evaluated first, then maxAllowedRepresentation,
     * i.e. the lowest value from executing these rules is used.
     *
     * This feature is typically used to reserve higher representations for playback only when connected over a fast connection.
     *
     * @param type String 'video' or 'audio' are the type options.
     * @param value number between 0 and 1, where 1 is allow all representations, and 0 is allow only the lowest.
     * @memberof module:MediaPlayer
     * @instance
     */
    function setMaxAllowedRepresentationRatioFor(type, value) {
        abrController.setMaxAllowedRepresentationRatioFor(type, value);
    }

    /**
     * @param type String 'video' or 'audio' are the type options.
     * @returns {number} The current representation ratio cap.
     * @memberof module:MediaPlayer
     * @see {@link MediaPlayer#setMaxAllowedRepresentationRatioFor setMaxAllowedRepresentationRatioFor()}
     * @instance
     */
    function getMaxAllowedRepresentationRatioFor(type) {
        return abrController.getMaxAllowedRepresentationRatioFor(type);
    }

    /**
     * <p>Set to false to prevent stream from auto-playing when the view is attached.</p>
     *
     * @param value {boolean}
     * @default true
     * @memberof module:MediaPlayer
     * @see {@link module:MediaPlayer#attachView attachView()}
     * @instance
     *
     */
    function setAutoPlay(value) {
        autoPlay = value;
    }

    /**
     * @returns {boolean} The current autoPlay state.
     * @memberof module:MediaPlayer
     * @instance
     */
    function getAutoPlay() {
        return autoPlay;
    }

    /**
     * Set to true if you would like dash.js to keep downloading fragments in the background
     * when the video element is paused.
     *
     * @default false
     * @param value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setScheduleWhilePaused(value) {
        mediaPlayerModel.setScheduleWhilePaused(value);
    }

    /**
     * Returns a boolean of the current state of ScheduleWhilePaused.
     * @returns {boolean}
     * @see {@link module:MediaPlayer#setScheduleWhilePaused setScheduleWhilePaused()}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getScheduleWhilePaused() {
        return mediaPlayerModel.getScheduleWhilePaused();
    }

    /**
     * Returns the DashMetrics.js Module. You use this Module to get access to all the public metrics
     * stored in dash.js
     *
     * @see {@link module:DashMetrics}
     * @returns {object}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getDashMetrics() {
        return dashMetrics;
    }

    /**
     *
     * @param type
     * @returns {object}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getMetricsFor(type) {
        return metricsModel.getReadOnlyMetricsFor(type);
    }

    /**
     * @param type
     * @returns {object}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getQualityFor(type) {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        return abrController.getQualityFor(type, streamController.getActiveStreamInfo());
    }

    /**
     * Sets the current quality for media type instead of letting the ABR Heuristics automatically selecting it..
     *
     * @param type
     * @param value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setQualityFor(type, value) {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        abrController.setPlaybackQuality(type, streamController.getActiveStreamInfo(), value);
    }

    /**
     * @memberof module:MediaPlayer
     * @instance
     */
    function getLimitBitrateByPortal() {
        return abrController.getLimitBitrateByPortal();
    }

    /**
     * Sets whether to limit the representation used based on the size of the playback area
     *
     * @param value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setLimitBitrateByPortal(value) {
        abrController.setLimitBitrateByPortal(value);
    }

    /**
     * Use this method to change the current text track for both external time text files and fragmented text tracks. There is no need to
     * set the track mode on the video object to switch a track when using this method.
     *
     * @param idx - Index of track based on the order of the order the tracks are added Use -1 to disable all tracks. (turn captions off).  Use module:MediaPlayer#dashjs.MediaPlayer.events.TEXT_TRACK_ADDED.
     * @see {@link module:MediaPlayer#dashjs.MediaPlayer.events.TEXT_TRACK_ADDED}
     * @memberof module:MediaPlayer
     * @instance
     */
    function setTextTrack(idx) {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        //For external time text file,  the only action needed to change a track is marking the track mode to showing.
        // Fragmented text tracks need the additional step of calling textSourceBuffer.setTextTrack();
        if (textSourceBuffer === undefined) {
            textSourceBuffer = (0, _TextSourceBufferJs2['default'])(context).getInstance();
        }

        var tracks = getVideoElement().textTracks;
        var ln = tracks.length;

        for (var i = 0; i < ln; i++) {
            var track = tracks[i];
            var mode = idx === i ? 'showing' : 'hidden';

            if (track.mode !== mode) {
                //checking that mode is not already set by 3rd Party player frameworks that set mode to prevent event retrigger.
                track.mode = mode;
            }
        }

        textSourceBuffer.setTextTrack();
    }

    /**
     * @param type
     * @returns {Array}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getBitrateInfoListFor(type) {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        var stream = getActiveStream();
        return stream ? stream.getBitrateListFor(type) : [];
    }

    /**
     * Use this method to explicitly set the starting bitrate for audio | video
     *
     * @param type
     * @param {number} value A value of the initial bitrate, kbps
     * @memberof module:MediaPlayer
     * @instance
     */
    function setInitialBitrateFor(type, value) {
        abrController.setInitialBitrateFor(type, value);
    }

    /**
     * @param type
     * @returns {number} A value of the initial bitrate, kbps
     * @memberof module:MediaPlayer
     * @instance
     */
    function getInitialBitrateFor(type) {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR; //abrController.getInitialBitrateFor is overloaded with ratioDict logic that needs manifest force it to not be callable pre play.
        }
        return abrController.getInitialBitrateFor(type);
    }

    /**
     * @param type
     * @param {number} value A value of the initial Representation Ratio
     * @memberof module:MediaPlayer
     * @instance
     */
    function setInitialRepresentationRatioFor(type, value) {
        abrController.setInitialRepresentationRatioFor(type, value);
    }

    /**
     * @param type
     * @returns {number} A value of the initial Representation Ratio
     * @memberof module:MediaPlayer
     * @instance
     */
    function getInitialRepresentationRatioFor(type) {
        return abrController.getInitialRepresentationRatioFor(type);
    }

    /**
     * This method returns the list of all available streams from a given manifest
     * @param manifest
     * @returns {Array} list of {@link StreamInfo}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getStreamsFromManifest(manifest) {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        return adapter.getStreamsInfo(manifest);
    }

    /**
     * This method returns the list of all available tracks for a given media type
     * @param type
     * @returns {Array} list of {@link MediaInfo}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getTracksFor(type) {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        var streamInfo = streamController.getActiveStreamInfo();
        if (!streamInfo) return [];
        return mediaController.getTracksFor(type, streamInfo);
    }

    /**
     * This method returns the list of all available tracks for a given media type and streamInfo from a given manifest
     * @param type
     * @param manifest
     * @param streamInfo
     * @returns {Array} list of {@link MediaInfo}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getTracksForTypeFromManifest(type, manifest, streamInfo) {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }

        streamInfo = streamInfo || adapter.getStreamsInfo(manifest)[0];

        return streamInfo ? adapter.getAllMediaInfoForType(manifest, streamInfo, type) : [];
    }

    /**
     * @param type
     * @returns {Object} {@link MediaInfo}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getCurrentTrackFor(type) {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        var streamInfo = streamController.getActiveStreamInfo();

        if (!streamInfo) return null;

        return mediaController.getCurrentTrackFor(type, streamInfo);
    }

    /**
     * This method allows to set media settings that will be used to pick the initial track. Format of the settings
     * is following:
     * {lang: langValue,
         *  viewpoint: viewpointValue,
         *  audioChannelConfiguration: audioChannelConfigurationValue,
         *  accessibility: accessibilityValue,
         *  role: roleValue}
     *
     *
     * @param type
     * @param value {Object}
     * @memberof module:MediaPlayer
     * @instance
     */
    function setInitialMediaSettingsFor(type, value) {
        mediaController.setInitialSettings(type, value);
    }

    /**
     * This method returns media settings that is used to pick the initial track. Format of the settings
     * is following:
     * {lang: langValue,
         *  viewpoint: viewpointValue,
         *  audioChannelConfiguration: audioChannelConfigurationValue,
         *  accessibility: accessibilityValue,
         *  role: roleValue}
     * @param type
     * @returns {Object}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getInitialMediaSettingsFor(type) {
        return mediaController.getInitialSettings(type);
    }

    /**
     * @param track instance of {@link MediaInfo}
     * @memberof module:MediaPlayer
     * @instance
     */
    function setCurrentTrack(track) {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        mediaController.setTrack(track);
    }

    /**
     * This method returns the current track switch mode.
     *
     * @param type
     * @returns mode
     * @memberof module:MediaPlayer
     * @instance
     */
    function getTrackSwitchModeFor(type) {
        return mediaController.getSwitchMode(type);
    }

    /**
     * This method sets the current track switch mode. Available options are:
     *
     * MediaController.TRACK_SWITCH_MODE_NEVER_REPLACE
     * (used to forbid clearing the buffered data (prior to current playback position) after track switch. Default for video)
     *
     * MediaController.TRACK_SWITCH_MODE_ALWAYS_REPLACE
     * (used to clear the buffered data (prior to current playback position) after track switch. Default for audio)
     *
     * @param type
     * @param mode
     * @memberof module:MediaPlayer
     * @instance
     */
    function setTrackSwitchModeFor(type, mode) {
        mediaController.setSwitchMode(type, mode);
    }

    /**
     * This method sets the selection mode for the initial track. This mode defines how the initial track will be selected
     * if no initial media settings are set. If initial media settings are set this parameter will be ignored. Available options are:
     *
     * MediaPlayer.dependencies.MediaController.trackSelectionModes.HIGHEST_BITRATE
     * this mode makes the player select the track with a highest bitrate. This mode is a default mode.
     *
     * MediaPlayer.dependencies.MediaController.trackSelectionModes.WIDEST_RANGE
     * this mode makes the player select the track with a widest range of bitrates
     *
     * @param mode
     * @memberof module:MediaPlayer
     * @instance
     */
    function setSelectionModeForInitialTrack(mode) {
        mediaController.setSelectionModeForInitialTrack(mode);
    }

    /**
     * This method returns the track selection mode.
     *
     * @returns mode
     * @memberof module:MediaPlayer
     * @instance
     */
    function getSelectionModeForInitialTrack() {
        return mediaController.getSelectionModeForInitialTrack();
    }

    /**
     * @deprecated since version 2.0 Instead use {@link module:MediaPlayer#getAutoSwitchQualityFor getAutoSwitchQualityFor()}.
     * @returns {boolean} Current state of adaptive bitrate switching
     * @memberof module:MediaPlayer
     * @instance
     */
    function getAutoSwitchQuality() {
        return abrController.getAutoSwitchBitrateFor('video') || abrController.getAutoSwitchBitrateFor('audio');
    }

    /**
     * Set to false to switch off adaptive bitrate switching.
     *
     * @deprecated since version 2.0 Instead use {@link module:MediaPlayer#setAutoSwitchQualityFor setAutoSwitchQualityFor()}.
     * @param value {boolean}
     * @default {boolean} true
     * @memberof module:MediaPlayer
     * @instance
     */
    function setAutoSwitchQuality(value) {
        abrController.setAutoSwitchBitrateFor('video', value);
        abrController.setAutoSwitchBitrateFor('audio', value);
    }

    /**
     * @param type {string} 'audio' | 'video'
     * @returns {boolean} Current state of adaptive bitrate switching
     * @memberof module:MediaPlayer
     * @instance
     */
    function getAutoSwitchQualityFor(type) {
        return abrController.getAutoSwitchBitrateFor(type);
    }

    /**
     * Set to false to switch off adaptive bitrate switching.
     *
     * @param type {string} 'audio' | 'video'
     * @param value {boolean}
     * @default {boolean} true
     * @memberof module:MediaPlayer
     * @instance
     */
    function setAutoSwitchQualityFor(type, value) {
        abrController.setAutoSwitchBitrateFor(type, value);
    }

    /**
     * Enabling buffer-occupancy ABR will switch to the *experimental* implementation of BOLA,
     * replacing the throughput-based ABR rule set (ThroughputRule, BufferOccupancyRule,
     * InsufficientBufferRule and AbandonRequestsRule) with the buffer-occupancy-based
     * BOLA rule set (BolaRule, BolaAbandonRule).
     *
     * @see {@link http://arxiv.org/abs/1601.06748 BOLA WhitePaper.}
     * @see {@link https://github.com/Dash-Industry-Forum/dash.js/wiki/BOLA-status More details about the implementation status.}
     * @param value {boolean}
     * @default false
     * @memberof module:MediaPlayer
     * @instance
     */
    function enableBufferOccupancyABR(value) {
        mediaPlayerModel.setBufferOccupancyABREnabled(value);
    }

    /**
     * Allows application to retrieve a manifest.  Manifest loading is asynchro
     * nous and
     * requires the app-provided callback function
     *
     * @param url {string} url the manifest url
     * @param callback {function} A Callback function provided when retrieving manifests
     * @memberof module:MediaPlayer
     * @instance
     */
    function retrieveManifest(url, callback) {
        var manifestLoader = createManifestLoader();
        var self = this;

        var handler = function handler(e) {
            if (!e.error) {
                callback(e.manifest);
            } else {
                callback(null, e.error);
            }
            eventBus.off(_coreEventsEventsJs2['default'].INTERNAL_MANIFEST_LOADED, handler, self);
            manifestLoader.reset();
        };

        eventBus.on(_coreEventsEventsJs2['default'].INTERNAL_MANIFEST_LOADED, handler, self);

        var uriQueryFragModel = (0, _modelsURIQueryAndFragmentModelJs2['default'])(context).getInstance();
        uriQueryFragModel.initialize();
        manifestLoader.load(uriQueryFragModel.parseURI(url));
    }

    /**
     * <p>Allows you to set a scheme and server source for UTC live edge detection for dynamic streams.
     * If UTCTiming is defined in the manifest, it will take precedence over any time source manually added.</p>
     * <p>If you have exposed the Date header, use the method {@link module:MediaPlayer#clearDefaultUTCTimingSources clearDefaultUTCTimingSources()}.
     * This will allow the date header on the manifest to be used instead of a time server</p>
     * @param {string} schemeIdUri -
     * <ul>
     * <li>urn:mpeg:dash:utc:http-head:2014</li>
     * <li>urn:mpeg:dash:utc:http-xsdate:2014</li>
     * <li>urn:mpeg:dash:utc:http-iso:2014</li>
     * <li>urn:mpeg:dash:utc:direct:2014</li>
     * </ul>
     * <p>Some specs referencing early ISO23009-1 drafts incorrectly use
     * 2012 in the URI, rather than 2014. support these for now.</p>
     * <ul>
     * <li>urn:mpeg:dash:utc:http-head:2012</li>
     * <li>urn:mpeg:dash:utc:http-xsdate:2012</li>
     * <li>urn:mpeg:dash:utc:http-iso:2012</li>
     * <li>urn:mpeg:dash:utc:direct:2012</li>
     * </ul>
     * @param {string} value - Path to a time source.
     * @default
     * <ul>
     *     <li>schemeIdUri:urn:mpeg:dash:utc:http-xsdate:2014</li>
     *     <li>value:http://time.akamai.com</li>
     * </ul>
     * @memberof module:MediaPlayer
     * @see {@link module:MediaPlayer#removeUTCTimingSource removeUTCTimingSource()}
     * @instance
     */
    function addUTCTimingSource(schemeIdUri, value) {
        removeUTCTimingSource(schemeIdUri, value); //check if it already exists and remove if so.
        var vo = new _dashVoUTCTimingJs2['default']();
        vo.schemeIdUri = schemeIdUri;
        vo.value = value;
        mediaPlayerModel.getUTCTimingSources().push(vo);
    }

    /**
     * <p>Allows you to remove a UTC time source. Both schemeIdUri and value need to match the Dash.vo.UTCTiming properties in order for the
     * entry to be removed from the array</p>
     * @param {string} schemeIdUri - see {@link module:MediaPlayer#addUTCTimingSource addUTCTimingSource()}
     * @param {string} value - see {@link module:MediaPlayer#addUTCTimingSource addUTCTimingSource()}
     * @memberof module:MediaPlayer
     * @see {@link module:MediaPlayer#clearDefaultUTCTimingSources clearDefaultUTCTimingSources()}
     * @instance
     */
    function removeUTCTimingSource(schemeIdUri, value) {
        var UTCTimingSources = mediaPlayerModel.getUTCTimingSources();
        UTCTimingSources.forEach(function (obj, idx) {
            if (obj.schemeIdUri === schemeIdUri && obj.value === value) {
                UTCTimingSources.splice(idx, 1);
            }
        });
    }

    /**
     * <p>Allows you to clear the stored array of time sources.</p>
     * <p>Example use: If you have exposed the Date header, calling this method
     * will allow the date header on the manifest to be used instead of the time server.</p>
     * <p>Example use: Calling this method, assuming there is not an exposed date header on the manifest,  will default back
     * to using a binary search to discover the live edge</p>
     *
     * @memberof module:MediaPlayer
     * @see {@link module:MediaPlayer#restoreDefaultUTCTimingSources restoreDefaultUTCTimingSources()}
     * @instance
     */
    function clearDefaultUTCTimingSources() {
        mediaPlayerModel.setUTCTimingSources([]);
    }

    /**
     * <p>Allows you to restore the default time sources after calling {@link module:MediaPlayer#clearDefaultUTCTimingSources clearDefaultUTCTimingSources()}</p>
     *
     * @default
     * <ul>
     *     <li>schemeIdUri:urn:mpeg:dash:utc:http-xsdate:2014</li>
     *     <li>value:http://time.akamai.com</li>
     * </ul>
     *
     * @memberof module:MediaPlayer
     * @see {@link module:MediaPlayer#addUTCTimingSource addUTCTimingSource()}
     * @instance
     */
    function restoreDefaultUTCTimingSources() {
        addUTCTimingSource(_modelsMediaPlayerModelJs2['default'].DEFAULT_UTC_TIMING_SOURCE.scheme, _modelsMediaPlayerModelJs2['default'].DEFAULT_UTC_TIMING_SOURCE.value);
    }

    /**
     * <p>Allows you to enable the use of the Date Header, if exposed with CORS, as a timing source for live edge detection. The
     * use of the date header will happen only after the other timing source that take precedence fail or are omitted as described.
     * {@link module:MediaPlayer#clearDefaultUTCTimingSources clearDefaultUTCTimingSources()} </p>
     *
     * @default {boolean} True
     * @memberof module:MediaPlayer
     * @see {@link module:MediaPlayer#addUTCTimingSource addUTCTimingSource()}
     * @instance
     */
    function enableManifestDateHeaderTimeSource(value) {
        mediaPlayerModel.setUseManifestDateHeaderTimeSource(value);
    }

    /**
     * This value influences the buffer pruning logic.
     * Allows you to modify the buffer that is kept in source buffer in seconds.
     *  0|-----------bufferToPrune-----------|-----bufferToKeep-----|currentTime|
     *
     * @default 30 seconds
     * @param {int} value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setBufferToKeep(value) {
        mediaPlayerModel.setBufferToKeep(value);
    }

    /**
     * This value influences the buffer pruning logic.
     * Allows you to modify the interval of pruning buffer in seconds.
     *
     * @default 30 seconds
     * @param {int} value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setBufferPruningInterval(value) {
        mediaPlayerModel.setBufferPruningInterval(value);
    }

    /**
     * The time that the internal buffer target will be set to post startup/seeks (NOT top quality).
     *
     * When the time is set higher than the default you will have to wait longer
     * to see automatic bitrate switches but will have a larger buffer which
     * will increase stability.
     *
     * @default 12 seconds.
     * @param {int} value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setStableBufferTime(value) {
        mediaPlayerModel.setStableBufferTime(value);
    }

    /**
     * The time that the internal buffer target will be set to once playing the top quality.
     * If there are multiple bitrates in your adaptation, and the media is playing at the highest
     * bitrate, then we try to build a larger buffer at the top quality to increase stability
     * and to maintain media quality.
     *
     * @default 30 seconds.
     * @param {int} value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setBufferTimeAtTopQuality(value) {
        mediaPlayerModel.setBufferTimeAtTopQuality(value);
    }

    /**
     * The time that the internal buffer target will be set to once playing the top quality for long form content.
     *
     * @default 60 seconds.
     * @see {@link module:MediaPlayer#setLongFormContentDurationThreshold setLongFormContentDurationThreshold()}
     * @see {@link module:MediaPlayer#setBufferTimeAtTopQuality setBufferTimeAtTopQuality()}
     * @param {int} value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setBufferTimeAtTopQualityLongForm(value) {
        mediaPlayerModel.setBufferTimeAtTopQualityLongForm(value);
    }

    /**
     * The threshold which defines if the media is considered long form content.
     * This will directly affect the buffer targets when playing back at the top quality.
     *
     * @see {@link module:MediaPlayer#setBufferTimeAtTopQualityLongForm setBufferTimeAtTopQualityLongForm()}
     * @default 600 seconds (10 minutes).
     * @param value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setLongFormContentDurationThreshold(value) {
        mediaPlayerModel.setLongFormContentDurationThreshold(value);
    }

    /**
     * A threshold, in seconds, of when dashjs abr becomes less conservative since we have a
     * larger "rich" buffer.
     * The BufferOccupancyRule.js rule will override the ThroughputRule's decision when the
     * buffer level surpasses this value and while it remains greater than this value.
     *
     * @default 20 seconds
     * @param value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setRichBufferThreshold(value) {
        mediaPlayerModel.setRichBufferThreshold(value);
    }

    /**
     * A percentage between 0.0 and 1 to reduce the measured throughput calculations.
     * The default is 0.9. The lower the value the more conservative and restricted the
     * measured throughput calculations will be. please use carefully. This will directly
     * affect the ABR logic in dash.js
     *
     * @param value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setBandwidthSafetyFactor(value) {
        mediaPlayerModel.setBandwidthSafetyFactor(value);
    }

    /**
     * Returns the number of the current BandwidthSafetyFactor
     *
     * @return {number} value
     * @see {@link module:MediaPlayer#setBandwidthSafetyFactor setBandwidthSafetyFactor()}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getBandwidthSafetyFactor() {
        return mediaPlayerModel.getBandwidthSafetyFactor();
    }

    /**
     * A timeout value in seconds, which during the ABRController will block switch-up events.
     * This will only take effect after an abandoned fragment event occurs.
     *
     * @default 10 seconds
     * @param {int} value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setAbandonLoadTimeout(value) {
        mediaPlayerModel.setAbandonLoadTimeout(value);
    }

    /**
     * Total number of retry attempts that will occur on a fragment load before it fails.
     * Increase this value to a maximum in order to achieve an automatic playback resume
     * in case of completely lost internet connection.
     *
     * @default 3
     * @param {int} value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setFragmentLoaderRetryAttempts(value) {
        mediaPlayerModel.setFragmentRetryAttempts(value);
    }

    /**
     * Time in milliseconds of which to reload a failed fragment load attempt.
     *
     * @default 1000 milliseconds
     * @param {int} value
     * @memberof module:MediaPlayer
     * @instance
     */
    function setFragmentLoaderRetryInterval(value) {
        mediaPlayerModel.setFragmentRetryInterval(value);
    }

    /**
     * Detects if Protection is included and returns an instance of ProtectionController.js
     * @memberof module:MediaPlayer
     * @instance
     */
    function getProtectionController() {
        return detectProtection();
    }

    /**
     * Will override dash.js protection controller.
     * @param {ProtectionController} [value] valid protection controller instance.
     * @memberof module:MediaPlayer
     * @instance
     */
    function attachProtectionController(value) {
        protectionController = value;
    }

    /**
     * @param {ProtectionData} [value] object containing
     * property names corresponding to key system name strings and associated
     * values being instances of.
     * @memberof module:MediaPlayer
     * @instance
     */
    function setProtectionData(value) {
        protectionData = value;
    }

    /**
     * This method serves to control captions z-index value. If 'true' is passed, the captions will have the highest z-index and be
     * displayed on top of other html elements. Default value is 'false' (z-index is not set).
     * @param value {Boolean}
     * @memberof module:MediaPlayer
     * @instance
     */
    function displayCaptionsOnTop(value) {
        var textTracks = (0, _TextTracksJs2['default'])(context).getInstance();
        textTracks.setConfig({ videoModel: videoModel });
        textTracks.initialize();
        textTracks.displayCConTop(value);
    }

    /**
     * Returns instance of Video Container that was attached by calling attachVideoContainer()
     * @returns {@link object}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getVideoContainer() {
        return videoModel ? videoModel.getVideoContainer() : null;
    }

    /**
     * Use this method to attach an HTML5 element that wraps the video element.
     *
     * @param container The HTML5 element containing the video element.
     * @memberof module:MediaPlayer
     * @instance
     */
    function attachVideoContainer(container) {
        if (!videoModel) {
            throw ELEMENT_NOT_ATTACHED_ERROR;
        }
        videoModel.setVideoContainer(container);
    }

    /**
     * Returns instance of Video Element that was attached by calling attachView()
     * @returns {object}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getVideoElement() {
        if (!videoModel) {
            throw ELEMENT_NOT_ATTACHED_ERROR;
        }
        return videoModel.getElement();
    }

    /**
     * Use this method to attach an HTML5 VideoElement for dash.js to operate upon.
     *
     * @param view An HTML5 VideoElement that has already been defined in the DOM.
     * @memberof module:MediaPlayer
     * @instance
     */
    function attachView(element) {
        if (!mediaPlayerInitialized) {
            throw MEDIA_PLAYER_NOT_INITIALIZED_ERROR;
        }
        videoModel = null;
        if (element) {
            videoModel = (0, _modelsVideoModelJs2['default'])(context).getInstance();
            videoModel.initialize();
            videoModel.setElement(element);
            detectProtection();
            detectMetricsReporting();
        }
        resetAndInitializePlayback();
    }

    /**
     * Returns instance of Div that was attached by calling attachTTMLRenderingDiv()
     * @returns {@link object}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getTTMLRenderingDiv() {
        return videoModel ? videoModel.getTTMLRenderingDiv() : null;
    }

    /**
     * Use this method to attach an HTML5 div for dash.js to render rich TTML subtitles.
     *
     * @param div An unstyled div placed after the video element. It will be styled to match the video size and overlay z-order.
     * @memberof module:MediaPlayer
     * @instance
     */
    function attachTTMLRenderingDiv(div) {
        if (!videoModel) {
            throw ELEMENT_NOT_ATTACHED_ERROR;
        }
        videoModel.setTTMLRenderingDiv(div);
    }

    /**
     * Returns the source string or manifest that was attached by calling attachSource()
     * @returns {string | manifest}
     * @memberof module:MediaPlayer
     * @instance
     */
    function getSource() {
        if (!source) {
            throw SOURCE_NOT_ATTACHED_ERROR;
        }
        return source;
    }

    /**
     * Use this method to set a source URL to a valid MPD manifest file OR
     * a previously downloaded and parsed manifest object.  Optionally, can
     * also provide protection information
     *
     * @param {string | object} urlOrManifest A URL to a valid MPD manifest file, or a
     * parsed manifest object.
     *
     *
     * @throw "MediaPlayer not initialized!"
     *
     * @memberof module:MediaPlayer
     * @instance
     */
    function attachSource(urlOrManifest) {
        if (!mediaPlayerInitialized) {
            throw MEDIA_PLAYER_NOT_INITIALIZED_ERROR;
        }

        if (typeof urlOrManifest === 'string') {
            var uriQueryFragModel = (0, _modelsURIQueryAndFragmentModelJs2['default'])(context).getInstance();
            uriQueryFragModel.initialize();
            source = uriQueryFragModel.parseURI(urlOrManifest);
        } else {
            source = urlOrManifest;
        }

        resetAndInitializePlayback();
    }

    /**
     * Sets the MPD source and the video element to null. You can also reset the MediaPlayer by
     * calling attachSource with a new source file.
     *
     * @memberof module:MediaPlayer
     * @instance
     */
    function reset() {
        attachSource(null);
        attachView(null);
    }

    //***********************************
    // PRIVATE METHODS
    //***********************************

    function resetAndInitializePlayback() {
        if (playbackInitialized) {
            playbackInitialized = false;
            adapter.reset();
            streamController.reset();
            playbackController.reset();
            abrController.reset();
            rulesController.reset();
            mediaController.reset();
            streamController = null;
            metricsReportingController = null;
            protectionController = null;
            protectionData = null;
            if (isReady()) {
                initializePlayback();
            }
        } else if (isReady()) {
            initializePlayback();
        }
    }

    function createControllers() {

        var synchronizationRulesCollection = (0, _rulesSynchronizationSynchronizationRulesCollectionJs2['default'])(context).getInstance();
        synchronizationRulesCollection.initialize();

        var abrRulesCollection = (0, _rulesAbrABRRulesCollectionJs2['default'])(context).getInstance();
        abrRulesCollection.initialize();

        //let scheduleRulesCollection = ScheduleRulesCollection(context).getInstance();
        //scheduleRulesCollection.initialize();

        var sourceBufferController = (0, _controllersSourceBufferControllerJs2['default'])(context).getInstance();
        sourceBufferController.setConfig({ dashManifestModel: dashManifestModel });

        var virtualBuffer = (0, _VirtualBufferJs2['default'])(context).getInstance();
        virtualBuffer.setConfig({
            sourceBufferController: sourceBufferController
        });

        mediaController.initialize();
        mediaController.setConfig({
            errHandler: errHandler
        });

        rulesController = (0, _rulesRulesControllerJs2['default'])(context).getInstance();
        rulesController.initialize();
        rulesController.setConfig({
            abrRulesCollection: abrRulesCollection,
            synchronizationRulesCollection: synchronizationRulesCollection
        });

        streamController = (0, _controllersStreamControllerJs2['default'])(context).getInstance();
        streamController.setConfig({
            capabilities: capabilities,
            manifestLoader: createManifestLoader(),
            manifestModel: manifestModel,
            dashManifestModel: dashManifestModel,
            protectionController: protectionController,
            adapter: adapter,
            metricsModel: metricsModel,
            dashMetrics: dashMetrics,
            liveEdgeFinder: (0, _utilsLiveEdgeFinderJs2['default'])(context).getInstance(),
            mediaSourceController: (0, _controllersMediaSourceControllerJs2['default'])(context).getInstance(),
            timeSyncController: (0, _controllersTimeSyncControllerJs2['default'])(context).getInstance(),
            baseURLController: (0, _controllersBaseURLControllerJs2['default'])(context).getInstance(),
            virtualBuffer: virtualBuffer,
            errHandler: errHandler,
            timelineConverter: (0, _dashUtilsTimelineConverterJs2['default'])(context).getInstance()
        });
        streamController.initialize(autoPlay, protectionData);

        abrController.setConfig({
            abrRulesCollection: abrRulesCollection,
            rulesController: rulesController,
            streamController: streamController
        });
    }

    function createManifestLoader() {
        return (0, _ManifestLoaderJs2['default'])(context).create({
            errHandler: errHandler,
            parser: createManifestParser(),
            metricsModel: metricsModel,
            requestModifier: (0, _utilsRequestModifierJs2['default'])(context).getInstance()
        });
    }

    function createManifestParser() {
        //TODO-Refactor Need to be able to switch this create out so will need API to set which parser to use?
        return (0, _dashDashParserJs2['default'])(context).create();
    }

    function createAdaptor() {
        //TODO-Refactor Need to be able to switch this create out so will need API to set which adapter to use? Handler is created is inside streamProcessor so need to figure that out as well
        adapter = (0, _dashDashAdapterJs2['default'])(context).getInstance();
        adapter.initialize();
        adapter.setConfig({ dashManifestModel: dashManifestModel });
        return adapter;
    }

    function detectProtection() {
        if (protectionController) {
            return protectionController;
        }
        // do not require Protection as dependencies as this is optional and intended to be loaded separately
        var Protection = dashjs.Protection; /* jshint ignore:line */
        if (typeof Protection == 'function') {
            //TODO need a better way to register/detect plugin components
            var protection = Protection(context).create();
            _coreEventsEventsJs2['default'].extend(Protection.events);
            _MediaPlayerEventsJs2['default'].extend(Protection.events, { publicOnly: true });
            protectionController = protection.createProtectionSystem({
                log: log,
                videoModel: videoModel,
                capabilities: capabilities,
                eventBus: eventBus,
                adapter: adapter
            });
            return protectionController;
        }

        return null;
    }

    function detectMetricsReporting() {
        if (metricsReportingController) {
            return metricsReportingController;
        }
        // do not require MetricsReporting as dependencies as this is optional and intended to be loaded separately
        var MetricsReporting = dashjs.MetricsReporting; /* jshint ignore:line */
        if (typeof MetricsReporting === 'function') {
            //TODO need a better way to register/detect plugin components
            var metricsReporting = MetricsReporting(context).create();

            metricsReportingController = metricsReporting.createMetricsReporting({
                log: log,
                eventBus: eventBus,
                mediaElement: getVideoElement(),
                dashManifestModel: dashManifestModel,
                metricsModel: metricsModel
            });

            return metricsReportingController;
        }

        return null;
    }

    function getDVRInfoMetric() {
        var metric = metricsModel.getReadOnlyMetricsFor('video') || metricsModel.getReadOnlyMetricsFor('audio');
        return dashMetrics.getCurrentDVRInfo(metric);
    }

    function getAsUTC(valToConvert) {
        var metric = getDVRInfoMetric();
        var availableFrom, utcValue;

        if (!metric) {
            return 0;
        }
        availableFrom = metric.manifestInfo.availableFrom.getTime() / 1000;
        utcValue = valToConvert + (availableFrom + metric.range.start);
        return utcValue;
    }

    function getActiveStream() {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        var streamInfo = streamController.getActiveStreamInfo();
        return streamInfo ? streamController.getStreamById(streamInfo.id) : null;
    }

    function getRepresentationListForActiveAdaptations(skipLoad) {
        if (!playbackInitialized) {
            throw PLAYBACK_NOT_INITIALIZED_ERROR;
        }
        var manifest = manifestModel.getValue();
        var s_info = streamController.getActiveStreamInfo();
        var p_idx = s_info.index;
        var periods = dashManifestModel.getRegularPeriods(manifest, dashManifestModel.getMpd(manifest));
        var adapts = [];
        var res = [];
        streamController.getStreamById(s_info.id).getProcessors().forEach(function (proc) {
            var m_info = proc.getMediaInfo();
            var a_idx = m_info.index;
            var adapt = dashManifestModel.getAdaptationForIndex(a_idx, manifest, p_idx);
            var adapt_s = { period: periods[p_idx], index: a_idx, proc: proc,
                type: dashManifestModel.getIsMuxed(adapt) ? 'muxed' : dashManifestModel.getIsVideo(adapt) ? 'video' : 'other' };
            adapts.push(adapt_s);
        });
        adapts.forEach(function (adapt) {
            var rs = dashManifestModel.getRepresentationsForAdaptation(manifest, adapt);
            rs.forEach(function (repr) {
                adapt.proc.getIndexHandler().updateRepresentation(repr, false, skipLoad);
                res.push(repr);
            });
        });
        return res;
    }

    function initializePlayback() {
        if (!playbackInitialized) {
            playbackInitialized = true;
            log('Playback Initialized');
            createControllers();
            if (typeof source === 'string') {
                streamController.load(source);
            } else {
                streamController.loadWithManifest(source);
            }
        }
    }

    instance = {
        initialize: initialize,
        on: on,
        off: off,
        extend: extend,
        attachView: attachView,
        attachSource: attachSource,
        isReady: isReady,
        play: play,
        isPaused: isPaused,
        pause: pause,
        isSeeking: isSeeking,
        seek: seek,
        setMute: setMute,
        isMuted: isMuted,
        setVolume: setVolume,
        getVolume: getVolume,
        time: time,
        duration: duration,
        timeAsUTC: timeAsUTC,
        durationAsUTC: durationAsUTC,
        getActiveStream: getActiveStream,
        getDVRWindowSize: getDVRWindowSize,
        getDVRSeekOffset: getDVRSeekOffset,
        convertToTimeCode: convertToTimeCode,
        formatUTC: formatUTC,
        getVersion: getVersion,
        getDebug: getDebug,
        getBufferLength: getBufferLength,
        getVideoModel: getVideoModel,
        getVideoContainer: getVideoContainer,
        getTTMLRenderingDiv: getTTMLRenderingDiv,
        getVideoElement: getVideoElement,
        getSource: getSource,
        setLiveDelayFragmentCount: setLiveDelayFragmentCount,
        setLiveDelay: setLiveDelay,
        useSuggestedPresentationDelay: useSuggestedPresentationDelay,
        enableLastBitrateCaching: enableLastBitrateCaching,
        enableLastMediaSettingsCaching: enableLastMediaSettingsCaching,
        setMaxAllowedBitrateFor: setMaxAllowedBitrateFor,
        getMaxAllowedBitrateFor: getMaxAllowedBitrateFor,
        setMaxAllowedRepresentationRatioFor: setMaxAllowedRepresentationRatioFor,
        getMaxAllowedRepresentationRatioFor: getMaxAllowedRepresentationRatioFor,
        setAutoPlay: setAutoPlay,
        getAutoPlay: getAutoPlay,
        setScheduleWhilePaused: setScheduleWhilePaused,
        getScheduleWhilePaused: getScheduleWhilePaused,
        getDashMetrics: getDashMetrics,
        getMetricsFor: getMetricsFor,
        getQualityFor: getQualityFor,
        setQualityFor: setQualityFor,
        getLimitBitrateByPortal: getLimitBitrateByPortal,
        setLimitBitrateByPortal: setLimitBitrateByPortal,
        setTextTrack: setTextTrack,
        getBitrateInfoListFor: getBitrateInfoListFor,
        setInitialBitrateFor: setInitialBitrateFor,
        getInitialBitrateFor: getInitialBitrateFor,
        setInitialRepresentationRatioFor: setInitialRepresentationRatioFor,
        getInitialRepresentationRatioFor: getInitialRepresentationRatioFor,
        getStreamsFromManifest: getStreamsFromManifest,
        getTracksFor: getTracksFor,
        getTracksForTypeFromManifest: getTracksForTypeFromManifest,
        getCurrentTrackFor: getCurrentTrackFor,
        setInitialMediaSettingsFor: setInitialMediaSettingsFor,
        getInitialMediaSettingsFor: getInitialMediaSettingsFor,
        setCurrentTrack: setCurrentTrack,
        getTrackSwitchModeFor: getTrackSwitchModeFor,
        setTrackSwitchModeFor: setTrackSwitchModeFor,
        setSelectionModeForInitialTrack: setSelectionModeForInitialTrack,
        getSelectionModeForInitialTrack: getSelectionModeForInitialTrack,
        getAutoSwitchQuality: getAutoSwitchQuality,
        setAutoSwitchQuality: setAutoSwitchQuality,
        getAutoSwitchQualityFor: getAutoSwitchQualityFor,
        setAutoSwitchQualityFor: setAutoSwitchQualityFor,
        enableBufferOccupancyABR: enableBufferOccupancyABR,
        setBandwidthSafetyFactor: setBandwidthSafetyFactor,
        getBandwidthSafetyFactor: getBandwidthSafetyFactor,
        setAbandonLoadTimeout: setAbandonLoadTimeout,
        retrieveManifest: retrieveManifest,
        addUTCTimingSource: addUTCTimingSource,
        removeUTCTimingSource: removeUTCTimingSource,
        clearDefaultUTCTimingSources: clearDefaultUTCTimingSources,
        restoreDefaultUTCTimingSources: restoreDefaultUTCTimingSources,
        setBufferToKeep: setBufferToKeep,
        setBufferPruningInterval: setBufferPruningInterval,
        setStableBufferTime: setStableBufferTime,
        setBufferTimeAtTopQuality: setBufferTimeAtTopQuality,
        setFragmentLoaderRetryAttempts: setFragmentLoaderRetryAttempts,
        setFragmentLoaderRetryInterval: setFragmentLoaderRetryInterval,
        setBufferTimeAtTopQualityLongForm: setBufferTimeAtTopQualityLongForm,
        setLongFormContentDurationThreshold: setLongFormContentDurationThreshold,
        setRichBufferThreshold: setRichBufferThreshold,
        getProtectionController: getProtectionController,
        attachProtectionController: attachProtectionController,
        setProtectionData: setProtectionData,
        enableManifestDateHeaderTimeSource: enableManifestDateHeaderTimeSource,
        displayCaptionsOnTop: displayCaptionsOnTop,
        attachVideoContainer: attachVideoContainer,
        attachTTMLRenderingDiv: attachTTMLRenderingDiv,
        getRepresentationListForActiveAdaptations: getRepresentationListForActiveAdaptations,
        reset: reset
    };

    setup();

    return instance;
}

MediaPlayer.__dashjs_factory_name = 'MediaPlayer';
var factory = _coreFactoryMakerJs2['default'].getClassFactory(MediaPlayer);
factory.events = _MediaPlayerEventsJs2['default'];
exports['default'] = factory;
module.exports = exports['default'];

},{"../core/FactoryMaker.js":7,"../dash/DashAdapter.js":11,"../dash/DashMetrics.js":13,"../dash/DashParser.js":14,"../dash/models/DashManifestModel.js":17,"../dash/utils/TimelineConverter.js":23,"../dash/vo/UTCTiming.js":33,"./../core/Debug.js":5,"./../core/EventBus.js":6,"./../core/events/Events.js":9,"./ManifestLoader.js":35,"./MediaPlayerEvents.js":38,"./TextSourceBuffer.js":41,"./TextTracks.js":42,"./VirtualBuffer.js":43,"./controllers/AbrController.js":46,"./controllers/BaseURLController.js":47,"./controllers/MediaController.js":52,"./controllers/MediaSourceController.js":53,"./controllers/PlaybackController.js":54,"./controllers/SourceBufferController.js":56,"./controllers/StreamController.js":57,"./controllers/TimeSyncController.js":59,"./models/ManifestModel.js":63,"./models/MediaPlayerModel.js":64,"./models/MetricsModel.js":65,"./models/URIQueryAndFragmentModel.js":66,"./models/VideoModel.js":67,"./rules/RulesController.js":69,"./rules/abr/ABRRulesCollection.js":71,"./rules/synchronization/SynchronizationRulesCollection.js":84,"./utils/Capabilities.js":87,"./utils/ErrorHandler.js":90,"./utils/LiveEdgeFinder.js":92,"./utils/RequestModifier.js":94}],38:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _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); } } };

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _coreEventsEventsBaseJs = _dereq_('../core/events/EventsBase.js');

var _coreEventsEventsBaseJs2 = _interopRequireDefault(_coreEventsEventsBaseJs);

/**
 * @Class
 *
 */

var MediaPlayerEvents = (function (_EventsBase) {
  _inherits(MediaPlayerEvents, _EventsBase);

  /**
   * @description Public facing external events to be used when developing a player that implements dash.js.
   */

  function MediaPlayerEvents() {
    _classCallCheck(this, MediaPlayerEvents);

    _get(Object.getPrototypeOf(MediaPlayerEvents.prototype), 'constructor', this).call(this);
    /**
     * Triggered when the video element's buffer state changes to stalled.
     * Check mediaType in payload to determine type (Video, Audio, FragmentedText).
     * @event MediaPlayerEvents#BUFFER_EMPTY
     */
    this.BUFFER_EMPTY = 'bufferstalled';
    /**
     * Triggered when the video element's buffer state changes to loaded.
     * Check mediaType in payload to determine type (Video, Audio, FragmentedText).
     * @event MediaPlayerEvents#BUFFER_LOADED
     */
    this.BUFFER_LOADED = 'bufferloaded';

    /**
     * Triggered when the video element's buffer state changes, either stalled or loaded. Check payload for state.
     * @event MediaPlayerEvents#BUFFER_LOADED
     */
    this.BUFFER_LEVEL_STATE_CHANGED = 'bufferStateChanged';

    /**
     * Triggered when
     * @event MediaPlayerEvents#ERROR
     */
    this.ERROR = 'error';
    /**
     * Triggered when {@link module:Debug} log method is called.
     * @event MediaPlayerEvents#LOG
     */
    this.LOG = 'log';
    //TODO refactor with internal event
    /**
     * Triggered when the manifest load is complete
     * @event MediaPlayerEvents#MANIFEST_LOADED
     */
    this.MANIFEST_LOADED = 'manifestloaded';
    /**
     * Triggered anytime there is a change to the overall metrics.
     * @event MediaPlayerEvents#METRICS_CHANGED
     */
    this.METRICS_CHANGED = 'metricschanged';
    /**
     * Triggered when an individual metric is added, updated or cleared.
     * @event MediaPlayerEvents#METRIC_CHANGED
     */
    this.METRIC_CHANGED = 'metricchanged';
    /**
     * Triggered every time a new metric is added.
     * @event MediaPlayerEvents#METRIC_ADDED
     */
    this.METRIC_ADDED = 'metricadded';
    /**
     * Triggered every time a metric is updated.
     * @event MediaPlayerEvents#METRIC_UPDATED
     */
    this.METRIC_UPDATED = 'metricupdated';
    /**
     * Triggered at the stream end of a period.
     * @event MediaPlayerEvents#PERIOD_SWITCH_COMPLETED
     */
    this.PERIOD_SWITCH_COMPLETED = 'streamswitchcompleted';
    /**
     * Triggered when a new period starts.
     * @event MediaPlayerEvents#PERIOD_SWITCH_STARTED
     */
    this.PERIOD_SWITCH_STARTED = 'streamswitchstarted';
    /**
     * Triggered when the stream is setup and ready.
     * @event MediaPlayerEvents#STREAM_INITIALIZED
     */
    this.STREAM_INITIALIZED = 'streaminitialized';
    /**
     * Triggered once all text tracks detected in the MPD are added to the video element.
     * @event MediaPlayerEvents#TEXT_TRACKS_ADDED
     */
    this.TEXT_TRACKS_ADDED = 'alltexttracksadded';
    /**
     * Triggered when a text track is added to the video element's TextTrackList
     * @event MediaPlayerEvents#TEXT_TRACK_ADDED
     */
    this.TEXT_TRACK_ADDED = 'texttrackadded';

    /**
     * Sent when enough data is available that the media can be played,
     * at least for a couple of frames.  This corresponds to the
     * HAVE_ENOUGH_DATA readyState.
     * @event MediaPlayerEvents#CAN_PLAY
     */
    this.CAN_PLAY = 'canPlay';

    /**
     * Sent when playback completes.
     * @event MediaPlayerEvents#PLAYBACK_ENDED
     */
    this.PLAYBACK_ENDED = 'playbackEnded';

    /**
     * Sent when an error occurs.  The element's error
     * attribute contains more information.
     * @event MediaPlayerEvents#PLAYBACK_ERROR
     */
    this.PLAYBACK_ERROR = 'playbackError';
    /**
     * The media's metadata has finished loading; all attributes now
     * contain as much useful information as they're going to.
     * @event MediaPlayerEvents#PLAYBACK_METADATA_LOADED
     */
    this.PLAYBACK_METADATA_LOADED = 'playbackMetaDataLoaded';
    /**
     * Sent when playback is paused.
     * @event MediaPlayerEvents#PLAYBACK_PAUSED
     */
    this.PLAYBACK_PAUSED = 'playbackPaused';
    /**
     * Sent when the media begins to play (either for the first time, after having been paused,
     * or after ending and then restarting).
     *
     * @event MediaPlayerEvents#PLAYBACK_PLAYING
     */
    this.PLAYBACK_PLAYING = 'playbackPlaying';
    /**
     * Sent periodically to inform interested parties of progress downloading
     * the media. Information about the current amount of the media that has
     * been downloaded is available in the media element's buffered attribute.
     * @event MediaPlayerEvents#PLAYBACK_PROGRESS
     */
    this.PLAYBACK_PROGRESS = 'playbackProgress';
    /**
     * Sent when the playback speed changes.
     * @event MediaPlayerEvents#PLAYBACK_RATE_CHANGED
     */
    this.PLAYBACK_RATE_CHANGED = 'playbackRateChanged';
    /**
     * Sent when a seek operation completes.
     * @event MediaPlayerEvents#PLAYBACK_SEEKED
     */
    this.PLAYBACK_SEEKED = 'playbackSeeked';
    /**
     * Sent when a seek operation begins.
     * @event MediaPlayerEvents#PLAYBACK_SEEKING
     */
    this.PLAYBACK_SEEKING = 'playbackSeeking';
    /**
     * Sent when playback of the media starts after having been paused;
     * that is, when playback is resumed after a prior pause event.
     *
     * @event MediaPlayerEvents#PLAYBACK_STARTED
     */
    this.PLAYBACK_STARTED = 'playbackStarted';
    /**
     * The time indicated by the element's currentTime attribute has changed.
     * @event MediaPlayerEvents#PLAYBACK_TIME_UPDATED
     */
    this.PLAYBACK_TIME_UPDATED = 'playbackTimeUpdated';
    this.SEGMENT_SYNC_SAMPLES = 'segmentSyncSamples';
  }

  return MediaPlayerEvents;
})(_coreEventsEventsBaseJs2['default']);

var mediaPlayerEvents = new MediaPlayerEvents();
exports['default'] = mediaPlayerEvents;
module.exports = exports['default'];

},{"../core/events/EventsBase.js":10}],39:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _utilsLiveEdgeFinderJs = _dereq_('./utils/LiveEdgeFinder.js');

var _utilsLiveEdgeFinderJs2 = _interopRequireDefault(_utilsLiveEdgeFinderJs);

var _StreamProcessorJs = _dereq_('./StreamProcessor.js');

var _StreamProcessorJs2 = _interopRequireDefault(_StreamProcessorJs);

var _controllersMediaControllerJs = _dereq_('./controllers/MediaController.js');

var _controllersMediaControllerJs2 = _interopRequireDefault(_controllersMediaControllerJs);

var _controllersEventControllerJs = _dereq_('./controllers/EventController.js');

var _controllersEventControllerJs2 = _interopRequireDefault(_controllersEventControllerJs);

var _controllersFragmentControllerJs = _dereq_('./controllers/FragmentController.js');

var _controllersFragmentControllerJs2 = _interopRequireDefault(_controllersFragmentControllerJs);

var _controllersAbrControllerJs = _dereq_('./controllers/AbrController.js');

var _controllersAbrControllerJs2 = _interopRequireDefault(_controllersAbrControllerJs);

var _modelsVideoModelJs = _dereq_('./models/VideoModel.js');

var _modelsVideoModelJs2 = _interopRequireDefault(_modelsVideoModelJs);

var _modelsMetricsModelJs = _dereq_('./models/MetricsModel.js');

var _modelsMetricsModelJs2 = _interopRequireDefault(_modelsMetricsModelJs);

var _controllersPlaybackControllerJs = _dereq_('./controllers/PlaybackController.js');

var _controllersPlaybackControllerJs2 = _interopRequireDefault(_controllersPlaybackControllerJs);

var _dashDashHandlerJs = _dereq_('../dash/DashHandler.js');

var _dashDashHandlerJs2 = _interopRequireDefault(_dashDashHandlerJs);

var _dashSegmentBaseLoaderJs = _dereq_('../dash/SegmentBaseLoader.js');

var _dashSegmentBaseLoaderJs2 = _interopRequireDefault(_dashSegmentBaseLoaderJs);

var _dashDashMetricsJs = _dereq_('../dash/DashMetrics.js');

var _dashDashMetricsJs2 = _interopRequireDefault(_dashDashMetricsJs);

var _coreEventBusJs = _dereq_('../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreDebugJs = _dereq_('../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _TextSourceBufferJs = _dereq_('./TextSourceBuffer.js');

var _TextSourceBufferJs2 = _interopRequireDefault(_TextSourceBufferJs);

function Stream(config) {

    var DATA_UPDATE_FAILED_ERROR_CODE = 1;

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var manifestModel = config.manifestModel;
    var manifestUpdater = config.manifestUpdater;
    var adapter = config.adapter;
    var capabilities = config.capabilities;
    var errHandler = config.errHandler;
    var timelineConverter = config.timelineConverter;
    var baseURLController = config.baseURLController;

    var instance = undefined,
        streamProcessors = undefined,
        isStreamActivated = undefined,
        isMediaInitialized = undefined,
        streamInfo = undefined,
        updateError = undefined,
        isUpdating = undefined,
        initialized = undefined,
        protectionController = undefined,
        liveEdgeFinder = undefined,
        playbackController = undefined,
        mediaController = undefined,
        fragmentController = undefined,
        eventController = undefined,
        abrController = undefined,
        textSourceBuffer = undefined;

    function setup() {
        streamProcessors = [];
        isStreamActivated = false;
        isMediaInitialized = false;
        streamInfo = null;
        updateError = {};
        isUpdating = false;
        initialized = false;

        liveEdgeFinder = (0, _utilsLiveEdgeFinderJs2['default'])(context).getInstance();
        playbackController = (0, _controllersPlaybackControllerJs2['default'])(context).getInstance();
        abrController = (0, _controllersAbrControllerJs2['default'])(context).getInstance();
        mediaController = (0, _controllersMediaControllerJs2['default'])(context).getInstance();
        fragmentController = (0, _controllersFragmentControllerJs2['default'])(context).create();
        textSourceBuffer = (0, _TextSourceBufferJs2['default'])(context).getInstance();

        eventBus.on(_coreEventsEventsJs2['default'].BUFFERING_COMPLETED, onBufferingCompleted, instance);
        eventBus.on(_coreEventsEventsJs2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, instance);
    }

    function initialize(StreamInfo, ProtectionController) {
        streamInfo = StreamInfo;
        protectionController = ProtectionController;
        if (protectionController) {
            eventBus.on(_coreEventsEventsJs2['default'].KEY_ERROR, onProtectionError, instance);
            eventBus.on(_coreEventsEventsJs2['default'].SERVER_CERTIFICATE_UPDATED, onProtectionError, instance);
            eventBus.on(_coreEventsEventsJs2['default'].LICENSE_REQUEST_COMPLETE, onProtectionError, instance);
            eventBus.on(_coreEventsEventsJs2['default'].KEY_SYSTEM_SELECTED, onProtectionError, instance);
            eventBus.on(_coreEventsEventsJs2['default'].KEY_SESSION_CREATED, onProtectionError, instance);
        }
    }

    /**
     * Activates Stream by re-initializing some of its components
     * @param mediaSource {MediaSource}
     * @memberof Stream#
     */
    function activate(mediaSource) {
        if (!isStreamActivated) {
            eventBus.on(_coreEventsEventsJs2['default'].CURRENT_TRACK_CHANGED, onCurrentTrackChanged, instance);
            initializeMedia(mediaSource);
        } else {
            createBuffers();
        }
    }

    /**
     * Partially resets some of the Stream elements
     * @memberof Stream#
     */
    function deactivate() {
        var ln = streamProcessors.length;
        for (var i = 0; i < ln; i++) {
            streamProcessors[i].reset();
        }
        streamProcessors = [];
        isStreamActivated = false;
        isMediaInitialized = false;
        clearEventController();
        eventBus.off(_coreEventsEventsJs2['default'].CURRENT_TRACK_CHANGED, onCurrentTrackChanged, instance);
    }

    function reset() {

        if (playbackController) {
            playbackController.pause();
            playbackController = null;
        }

        if (fragmentController) {
            fragmentController.reset();
            fragmentController = null;
        }

        liveEdgeFinder.abortSearch();
        deactivate();

        mediaController = null;
        abrController = null;
        manifestUpdater = null;
        manifestModel = null;
        adapter = null;
        capabilities = null;
        log = null;
        errHandler = null;
        isUpdating = false;
        initialized = false;
        updateError = {};

        eventBus.off(_coreEventsEventsJs2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, instance);
        eventBus.off(_coreEventsEventsJs2['default'].BUFFERING_COMPLETED, onBufferingCompleted, instance);
        eventBus.off(_coreEventsEventsJs2['default'].KEY_ERROR, onProtectionError, instance);
        eventBus.off(_coreEventsEventsJs2['default'].SERVER_CERTIFICATE_UPDATED, onProtectionError, instance);
        eventBus.off(_coreEventsEventsJs2['default'].LICENSE_REQUEST_COMPLETE, onProtectionError, instance);
        eventBus.off(_coreEventsEventsJs2['default'].KEY_SYSTEM_SELECTED, onProtectionError, instance);
        eventBus.off(_coreEventsEventsJs2['default'].KEY_SESSION_CREATED, onProtectionError, instance);
    }

    function getDuration() {
        return streamInfo.duration;
    }

    function getStartTime() {
        return streamInfo.start;
    }

    function getStreamIndex() {
        return streamInfo.index;
    }

    function getId() {
        return streamInfo.id;
    }

    function getStreamInfo() {
        return streamInfo;
    }

    function hasMedia(type) {
        return getMediaInfo(type) !== null;
    }

    /**
     * @param type
     * @returns {Array}
     * @memberof Stream#
     */
    function getBitrateListFor(type) {
        var mediaInfo = getMediaInfo(type);
        return abrController.getBitrateList(mediaInfo);
    }

    function startEventController() {
        if (eventController) {
            eventController.start();
        }
    }

    function clearEventController() {
        if (eventController) {
            eventController.clear();
        }
    }

    function isActivated() {
        return isStreamActivated;
    }

    function isInitialized() {
        return initialized;
    }

    function onProtectionError(event) {
        if (event.error) {
            errHandler.mediaKeySessionError(event.error);
            log(event.error);
            reset();
        }
    }

    function getMimeTypeOrType(mediaInfo) {
        return mediaInfo.type === 'text' ? mediaInfo.mimeType : mediaInfo.type;
    }

    function isMediaSupported(mediaInfo, mediaSource, manifest) {
        var type = mediaInfo.type;
        var codec, msg;

        if (type === 'muxed' && mediaInfo) {
            msg = 'Multiplexed representations are intentionally not supported, as they are not compliant with the DASH-AVC/264 guidelines';
            log(msg);
            errHandler.manifestError(msg, 'multiplexedrep', manifestModel.getValue());
            return false;
        }

        if (type === 'text' || type === 'fragmentedText' || type === 'embeddedText') return true;

        codec = mediaInfo.codec;
        log(type + ' codec: ' + codec);

        if (!!mediaInfo.contentProtection && !capabilities.supportsEncryptedMedia()) {
            errHandler.capabilityError('encryptedmedia');
        } else if (!capabilities.supportsCodec((0, _modelsVideoModelJs2['default'])(context).getInstance().getElement(), codec)) {
            msg = type + 'Codec (' + codec + ') is not supported.';
            errHandler.manifestError(msg, 'codec', manifest);
            log(msg);
            return false;
        }

        return true;
    }

    function onCurrentTrackChanged(e) {
        if (e.newMediaInfo.streamInfo.id !== streamInfo.id) return;

        var processor = getProcessorForMediaInfo(e.oldMediaInfo);
        if (!processor) return;

        var currentTime = playbackController.getTime();
        var buffer = processor.getBuffer();
        var mediaInfo = e.newMediaInfo;
        var manifest = manifestModel.getValue();
        var idx = streamProcessors.indexOf(processor);
        var mediaSource = processor.getMediaSource();

        if (mediaInfo.type !== 'fragmentedText') {
            processor.reset(true);
            createStreamProcessor(mediaInfo, manifest, mediaSource, { buffer: buffer, replaceIdx: idx, currentTime: currentTime });
            playbackController.seek(playbackController.getTime());
        } else {
            processor.updateMediaInfo(manifest, mediaInfo);
        }
    }

    function createIndexHandler() {

        var segmentBaseLoader = (0, _dashSegmentBaseLoaderJs2['default'])(context).getInstance();
        segmentBaseLoader.setConfig({
            baseURLController: baseURLController
        });
        segmentBaseLoader.initialize();

        var handler = (0, _dashDashHandlerJs2['default'])(context).create({
            segmentBaseLoader: segmentBaseLoader,
            timelineConverter: timelineConverter,
            dashMetrics: (0, _dashDashMetricsJs2['default'])(context).getInstance(),
            metricsModel: (0, _modelsMetricsModelJs2['default'])(context).getInstance(),
            baseURLController: baseURLController
        });

        return handler;
    }

    function createStreamProcessor(mediaInfo, manifest, mediaSource, optionalSettings) {
        var streamProcessor = (0, _StreamProcessorJs2['default'])(context).create({
            indexHandler: createIndexHandler(),
            timelineConverter: timelineConverter,
            adapter: adapter,
            manifestModel: manifestModel
        });

        var allMediaForType = adapter.getAllMediaInfoForType(manifest, streamInfo, mediaInfo.type);
        streamProcessor.initialize(getMimeTypeOrType(mediaInfo), fragmentController, mediaSource, instance, eventController);
        abrController.updateTopQualityIndex(mediaInfo);

        if (optionalSettings) {
            streamProcessor.setBuffer(optionalSettings.buffer);
            streamProcessors[optionalSettings.replaceIdx] = streamProcessor;
        } else {
            streamProcessors.push(streamProcessor);
        }

        if (mediaInfo.type === 'text' || mediaInfo.type === 'fragmentedText') {
            var idx;
            for (var i = 0; i < allMediaForType.length; i++) {
                if (allMediaForType[i].index === mediaInfo.index) {
                    idx = i;
                }
                streamProcessor.updateMediaInfo(manifest, allMediaForType[i]); //creates text tracks for all adaptations in one stream processor
            }
            if (mediaInfo.type === 'fragmentedText') {
                streamProcessor.updateMediaInfo(manifest, allMediaForType[idx]); //sets the initial media info
            }
        } else {
                streamProcessor.updateMediaInfo(manifest, mediaInfo);
            }

        return streamProcessor;
    }

    function initializeMediaForType(type, mediaSource) {
        var manifest = manifestModel.getValue();
        var allMediaForType = adapter.getAllMediaInfoForType(manifest, streamInfo, type);

        var mediaInfo = null;
        var initialMediaInfo;

        if (!allMediaForType || allMediaForType.length === 0) {
            log('No ' + type + ' data.');
            return;
        }

        for (var i = 0, ln = allMediaForType.length; i < ln; i++) {
            mediaInfo = allMediaForType[i];

            if (type === 'embeddedText') {
                textSourceBuffer.addEmbeddedTrack(mediaInfo);
            } else {
                if (!isMediaSupported(mediaInfo, mediaSource, manifest)) continue;

                if (mediaController.isMultiTrackSupportedByType(mediaInfo.type)) {
                    mediaController.addTrack(mediaInfo, streamInfo);
                }
            }
        }

        if (type === 'embeddedText' || mediaController.getTracksFor(type, streamInfo).length === 0) {
            return;
        }

        mediaController.checkInitialMediaSettingsForType(type, streamInfo);
        initialMediaInfo = mediaController.getCurrentTrackFor(type, streamInfo);

        // TODO : How to tell index handler live/duration?
        // TODO : Pass to controller and then pass to each method on handler?

        createStreamProcessor(initialMediaInfo, manifest, mediaSource);
    }

    function initializeMedia(mediaSource) {
        var manifest = manifestModel.getValue();
        var events;

        eventController = (0, _controllersEventControllerJs2['default'])(context).getInstance();
        eventController.initialize();
        eventController.setConfig({
            manifestModel: manifestModel,
            manifestUpdater: manifestUpdater
        });
        events = adapter.getEventsFor(manifest, streamInfo);
        eventController.addInlineEvents(events);

        isUpdating = true;
        initializeMediaForType('video', mediaSource);
        initializeMediaForType('audio', mediaSource);
        initializeMediaForType('text', mediaSource);
        initializeMediaForType('fragmentedText', mediaSource);
        initializeMediaForType('embeddedText', mediaSource);
        initializeMediaForType('muxed', mediaSource);

        createBuffers();

        //TODO. Consider initialization of TextSourceBuffer here if embeddedText, but no sideloadedText.

        isMediaInitialized = true;
        isUpdating = false;

        if (streamProcessors.length === 0) {
            var msg = 'No streams to play.';
            errHandler.manifestError(msg, 'nostreams', manifest);
            log(msg);
        } else {
            liveEdgeFinder.initialize(timelineConverter, streamProcessors[0]);
            //log("Playback initialized!");
            checkIfInitializationCompleted();
        }
    }

    function checkIfInitializationCompleted() {
        var ln = streamProcessors.length;
        var hasError = !!updateError.audio || !!updateError.video;
        var error = hasError ? new Error(DATA_UPDATE_FAILED_ERROR_CODE, 'Data update failed', null) : null;
        var i = 0;

        for (i; i < ln; i++) {
            if (streamProcessors[i].isUpdating() || isUpdating) return;
        }

        initialized = true;
        isStreamActivated = true;
        if (!isMediaInitialized) return;
        if (protectionController) {
            protectionController.initialize(manifestModel.getValue(), getMediaInfo('audio'), getMediaInfo('video'));
        }
        eventBus.trigger(_coreEventsEventsJs2['default'].STREAM_INITIALIZED, { streamInfo: streamInfo, error: error });
    }

    function getMediaInfo(type) {
        var ln = streamProcessors.length;
        var mediaCtrl = null;

        for (var i = 0; i < ln; i++) {
            mediaCtrl = streamProcessors[i];

            if (mediaCtrl.getType() === type) return mediaCtrl.getMediaInfo();
        }

        return null;
    }

    function createBuffers() {
        for (var i = 0, ln = streamProcessors.length; i < ln; i++) {
            streamProcessors[i].createBuffer();
        }
    }

    function onBufferingCompleted(e) {
        if (e.streamInfo !== streamInfo) return;

        var processors = getProcessors();
        var ln = processors.length;
        var i = 0;

        // if there is at least one buffer controller that has not completed buffering yet do nothing
        for (i; i < ln; i++) {
            if (!processors[i].isBufferingCompleted()) return;
        }

        eventBus.trigger(_coreEventsEventsJs2['default'].STREAM_BUFFERING_COMPLETED, { streamInfo: streamInfo });
    }

    function onDataUpdateCompleted(e) {
        var sp = e.sender.getStreamProcessor();

        if (sp.getStreamInfo() !== streamInfo) return;

        updateError[sp.getType()] = e.error;
        checkIfInitializationCompleted();
    }

    function getProcessorForMediaInfo(mediaInfo) {
        if (!mediaInfo) return false;

        var processors = getProcessors();

        return processors.filter(function (processor) {
            return processor.getType() === mediaInfo.type;
        })[0];
    }

    function getProcessors() {
        var ln = streamProcessors.length;
        var arr = [];
        var i = 0;

        var type, controller;

        for (i; i < ln; i++) {
            controller = streamProcessors[i];
            type = controller.getType();

            if (type === 'audio' || type === 'video' || type === 'fragmentedText') {
                arr.push(controller);
            }
        }

        return arr;
    }

    function updateData(updatedStreamInfo) {
        var ln = streamProcessors.length;
        var manifest = manifestModel.getValue();

        var i = 0;
        var mediaInfo, events, controller;

        isStreamActivated = false;
        streamInfo = updatedStreamInfo;
        log('Manifest updated... set new data on buffers.');

        if (eventController) {
            events = adapter.getEventsFor(manifest, streamInfo);
            eventController.addInlineEvents(events);
        }

        isUpdating = true;
        initialized = false;

        for (i; i < ln; i++) {
            controller = streamProcessors[i];
            mediaInfo = adapter.getMediaInfoForType(manifest, streamInfo, controller.getType());
            abrController.updateTopQualityIndex(mediaInfo);
            controller.updateMediaInfo(manifest, mediaInfo);
        }

        isUpdating = false;
        checkIfInitializationCompleted();
    }

    instance = {
        initialize: initialize,
        activate: activate,
        deactivate: deactivate,
        getDuration: getDuration,
        getStartTime: getStartTime,
        getStreamIndex: getStreamIndex,
        getId: getId,
        getStreamInfo: getStreamInfo,
        hasMedia: hasMedia,
        getBitrateListFor: getBitrateListFor,
        startEventController: startEventController,
        isActivated: isActivated,
        isInitialized: isInitialized,
        updateData: updateData,
        reset: reset,
        getProcessors: getProcessors
    };

    setup();
    return instance;
}

Stream.__dashjs_factory_name = 'Stream';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(Stream);
module.exports = exports['default'];

},{"../core/Debug.js":5,"../core/EventBus.js":6,"../core/FactoryMaker.js":7,"../core/events/Events.js":9,"../dash/DashHandler.js":12,"../dash/DashMetrics.js":13,"../dash/SegmentBaseLoader.js":15,"./StreamProcessor.js":40,"./TextSourceBuffer.js":41,"./controllers/AbrController.js":46,"./controllers/EventController.js":50,"./controllers/FragmentController.js":51,"./controllers/MediaController.js":52,"./controllers/PlaybackController.js":54,"./models/MetricsModel.js":65,"./models/VideoModel.js":67,"./utils/LiveEdgeFinder.js":92}],40:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _controllersAbrControllerJs = _dereq_('./controllers/AbrController.js');

var _controllersAbrControllerJs2 = _interopRequireDefault(_controllersAbrControllerJs);

var _controllersBufferControllerJs = _dereq_('./controllers/BufferController.js');

var _controllersBufferControllerJs2 = _interopRequireDefault(_controllersBufferControllerJs);

var _controllersStreamControllerJs = _dereq_('./controllers/StreamController.js');

var _controllersStreamControllerJs2 = _interopRequireDefault(_controllersStreamControllerJs);

var _controllersMediaControllerJs = _dereq_('./controllers/MediaController.js');

var _controllersMediaControllerJs2 = _interopRequireDefault(_controllersMediaControllerJs);

var _controllersTextControllerJs = _dereq_('./controllers/TextController.js');

var _controllersTextControllerJs2 = _interopRequireDefault(_controllersTextControllerJs);

var _controllersScheduleControllerJs = _dereq_('./controllers/ScheduleController.js');

var _controllersScheduleControllerJs2 = _interopRequireDefault(_controllersScheduleControllerJs);

var _rulesRulesControllerJs = _dereq_('./rules/RulesController.js');

var _rulesRulesControllerJs2 = _interopRequireDefault(_rulesRulesControllerJs);

var _modelsMediaPlayerModelJs = _dereq_('./models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

var _modelsMetricsModelJs = _dereq_('./models/MetricsModel.js');

var _modelsMetricsModelJs2 = _interopRequireDefault(_modelsMetricsModelJs);

var _FragmentLoaderJs = _dereq_('./FragmentLoader.js');

var _FragmentLoaderJs2 = _interopRequireDefault(_FragmentLoaderJs);

var _utilsRequestModifierJs = _dereq_('./utils/RequestModifier.js');

var _utilsRequestModifierJs2 = _interopRequireDefault(_utilsRequestModifierJs);

var _controllersSourceBufferController = _dereq_('./controllers/SourceBufferController');

var _controllersSourceBufferController2 = _interopRequireDefault(_controllersSourceBufferController);

var _TextSourceBufferJs = _dereq_('./TextSourceBuffer.js');

var _TextSourceBufferJs2 = _interopRequireDefault(_TextSourceBufferJs);

var _VirtualBufferJs = _dereq_('./VirtualBuffer.js');

var _VirtualBufferJs2 = _interopRequireDefault(_VirtualBufferJs);

var _controllersMediaSourceControllerJs = _dereq_('./controllers/MediaSourceController.js');

var _controllersMediaSourceControllerJs2 = _interopRequireDefault(_controllersMediaSourceControllerJs);

var _dashModelsDashManifestModelJs = _dereq_('../dash/models/DashManifestModel.js');

var _dashModelsDashManifestModelJs2 = _interopRequireDefault(_dashModelsDashManifestModelJs);

var _dashDashMetricsJs = _dereq_('../dash/DashMetrics.js');

var _dashDashMetricsJs2 = _interopRequireDefault(_dashDashMetricsJs);

var _dashControllersRepresentationControllerJs = _dereq_('../dash/controllers/RepresentationController.js');

var _dashControllersRepresentationControllerJs2 = _interopRequireDefault(_dashControllersRepresentationControllerJs);

var _utilsErrorHandlerJs = _dereq_('./utils/ErrorHandler.js');

var _utilsErrorHandlerJs2 = _interopRequireDefault(_utilsErrorHandlerJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function StreamProcessor(config) {

    var context = this.context;

    var indexHandler = config.indexHandler;
    var timelineConverter = config.timelineConverter;
    var adapter = config.adapter;
    var manifestModel = config.manifestModel;

    var instance = undefined,
        dynamic = undefined,
        mediaInfo = undefined,
        type = undefined,
        mediaInfoArr = undefined,
        stream = undefined,
        eventController = undefined,
        abrController = undefined,
        bufferController = undefined,
        scheduleController = undefined,
        representationController = undefined,
        fragmentController = undefined,
        fragmentLoader = undefined,
        fragmentModel = undefined;

    function setup() {
        mediaInfoArr = [];
    }

    function initialize(Type, FragmentController, mediaSource, Stream, EventController) {

        type = Type;
        stream = Stream;
        eventController = EventController;
        fragmentController = FragmentController;
        dynamic = stream.getStreamInfo().manifestInfo.isDynamic;

        indexHandler.initialize(this);

        abrController = (0, _controllersAbrControllerJs2['default'])(context).getInstance();
        abrController.initialize(type, this);

        bufferController = createBufferControllerForType(Type);
        bufferController.initialize(type, mediaSource, this);

        scheduleController = (0, _controllersScheduleControllerJs2['default'])(context).create({
            metricsModel: (0, _modelsMetricsModelJs2['default'])(context).getInstance(),
            manifestModel: manifestModel,
            adapter: adapter,
            dashMetrics: (0, _dashDashMetricsJs2['default'])(context).getInstance(),
            dashManifestModel: (0, _dashModelsDashManifestModelJs2['default'])(context).getInstance(),
            timelineConverter: timelineConverter,
            rulesController: (0, _rulesRulesControllerJs2['default'])(context).getInstance(),
            mediaPlayerModel: (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance()
        });

        scheduleController.initialize(type, this);

        fragmentLoader = (0, _FragmentLoaderJs2['default'])(context).create({
            metricsModel: (0, _modelsMetricsModelJs2['default'])(context).getInstance(),
            errHandler: (0, _utilsErrorHandlerJs2['default'])(context).getInstance(),
            requestModifier: (0, _utilsRequestModifierJs2['default'])(context).getInstance()
        });

        representationController = (0, _dashControllersRepresentationControllerJs2['default'])(context).create();
        representationController.initialize(this);

        fragmentModel = scheduleController.getFragmentModel();
        fragmentModel.setLoader(fragmentLoader);
    }

    function reset(errored) {
        if (fragmentModel) {
            fragmentModel.reset();
            fragmentModel = null;
        }

        indexHandler.reset();

        if (bufferController) {
            bufferController.reset(errored);
            bufferController = null;
        }

        if (scheduleController) {
            scheduleController.reset();
            scheduleController = null;
        }

        if (representationController) {
            representationController.reset();
            representationController = null;
        }

        fragmentController = null;
        fragmentLoader = null;

        eventController = null;
        stream = null;
        dynamic = null;
        mediaInfo = null;
        mediaInfoArr = [];
        type = null;
    }

    function isUpdating() {
        return representationController.isUpdating();
    }

    function getType() {
        return type;
    }

    function getABRController() {
        return abrController;
    }

    function getRepresentationController() {
        return representationController;
    }

    function getFragmentLoader() {
        return fragmentLoader;
    }

    function getIndexHandler() {
        return indexHandler;
    }

    function getFragmentController() {
        return fragmentController;
    }

    function getBuffer() {
        return bufferController.getBuffer();
    }

    function setBuffer(buffer) {
        bufferController.setBuffer(buffer);
    }

    function getBufferController() {
        return bufferController;
    }

    function getFragmentModel() {
        return fragmentModel;
    }

    function getStreamInfo() {
        return stream.getStreamInfo();
    }

    function updateMediaInfo(manifest, newMediaInfo) {
        if (newMediaInfo !== mediaInfo && (!newMediaInfo || !mediaInfo || newMediaInfo.type === mediaInfo.type)) {
            mediaInfo = newMediaInfo;
        }
        if (mediaInfoArr.indexOf(newMediaInfo) === -1) {
            mediaInfoArr.push(newMediaInfo);
        }
        adapter.updateData(manifest, this);
    }

    function getMediaInfoArr() {
        return mediaInfoArr;
    }

    function getMediaInfo() {
        return mediaInfo;
    }

    function getMediaSource() {
        return bufferController.getMediaSource();
    }

    function getScheduleController() {
        return scheduleController;
    }

    function getEventController() {
        return eventController;
    }

    function start() {
        scheduleController.start();
    }

    function stop() {
        scheduleController.stop();
    }

    function getCurrentRepresentationInfo() {
        return adapter.getCurrentRepresentationInfo(manifestModel.getValue(), representationController);
    }

    function getRepresentationInfoForQuality(quality) {
        return adapter.getRepresentationInfoForQuality(manifestModel.getValue(), representationController, quality);
    }

    function isBufferingCompleted() {
        return bufferController.getIsBufferingCompleted();
    }

    function createBuffer() {
        return bufferController.getBuffer() || bufferController.createBuffer(mediaInfo);
    }

    function isDynamic() {
        return dynamic;
    }

    function createBufferControllerForType(type) {
        var controller = null;

        if (type === 'video' || type === 'audio' || type === 'fragmentedText') {
            controller = (0, _controllersBufferControllerJs2['default'])(context).create({
                metricsModel: (0, _modelsMetricsModelJs2['default'])(context).getInstance(),
                manifestModel: manifestModel,
                sourceBufferController: (0, _controllersSourceBufferController2['default'])(context).getInstance(),
                errHandler: (0, _utilsErrorHandlerJs2['default'])(context).getInstance(),
                mediaSourceController: (0, _controllersMediaSourceControllerJs2['default'])(context).getInstance(),
                streamController: (0, _controllersStreamControllerJs2['default'])(context).getInstance(),
                mediaController: (0, _controllersMediaControllerJs2['default'])(context).getInstance(),
                adapter: adapter,
                virtualBuffer: (0, _VirtualBufferJs2['default'])(context).getInstance(),
                textSourceBuffer: (0, _TextSourceBufferJs2['default'])(context).getInstance()
            });
        } else {
            controller = (0, _controllersTextControllerJs2['default'])(context).create({
                errHandler: (0, _utilsErrorHandlerJs2['default'])(context).getInstance(),
                sourceBufferController: (0, _controllersSourceBufferController2['default'])(context).getInstance()
            });
        }

        return controller;
    }

    instance = {
        initialize: initialize,
        isUpdating: isUpdating,
        getType: getType,
        getBufferController: getBufferController,
        getABRController: getABRController,
        getFragmentLoader: getFragmentLoader,
        getFragmentModel: getFragmentModel,
        getScheduleController: getScheduleController,
        getEventController: getEventController,
        getFragmentController: getFragmentController,
        getRepresentationController: getRepresentationController,
        getIndexHandler: getIndexHandler,
        getCurrentRepresentationInfo: getCurrentRepresentationInfo,
        getRepresentationInfoForQuality: getRepresentationInfoForQuality,
        isBufferingCompleted: isBufferingCompleted,
        createBuffer: createBuffer,
        getStreamInfo: getStreamInfo,
        updateMediaInfo: updateMediaInfo,
        getMediaInfoArr: getMediaInfoArr,
        getMediaInfo: getMediaInfo,
        getMediaSource: getMediaSource,
        getBuffer: getBuffer,
        setBuffer: setBuffer,
        start: start,
        stop: stop,
        isDynamic: isDynamic,
        reset: reset
    };

    setup();
    return instance;
}
StreamProcessor.__dashjs_factory_name = 'StreamProcessor';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(StreamProcessor);
module.exports = exports['default'];

},{"../core/FactoryMaker.js":7,"../dash/DashMetrics.js":13,"../dash/controllers/RepresentationController.js":16,"../dash/models/DashManifestModel.js":17,"./FragmentLoader.js":34,"./TextSourceBuffer.js":41,"./VirtualBuffer.js":43,"./controllers/AbrController.js":46,"./controllers/BufferController.js":49,"./controllers/MediaController.js":52,"./controllers/MediaSourceController.js":53,"./controllers/ScheduleController.js":55,"./controllers/SourceBufferController":56,"./controllers/StreamController.js":57,"./controllers/TextController.js":58,"./models/MediaPlayerModel.js":64,"./models/MetricsModel.js":65,"./rules/RulesController.js":69,"./utils/ErrorHandler.js":90,"./utils/RequestModifier.js":94}],41:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _voTextTrackInfoJs = _dereq_('./vo/TextTrackInfo.js');

var _voTextTrackInfoJs2 = _interopRequireDefault(_voTextTrackInfoJs);

var _dashUtilsFragmentedTextBoxParserJs = _dereq_('../dash/utils/FragmentedTextBoxParser.js');

var _dashUtilsFragmentedTextBoxParserJs2 = _interopRequireDefault(_dashUtilsFragmentedTextBoxParserJs);

var _utilsBoxParserJs = _dereq_('./utils/BoxParser.js');

var _utilsBoxParserJs2 = _interopRequireDefault(_utilsBoxParserJs);

var _utilsCustomTimeRangesJs = _dereq_('./utils/CustomTimeRanges.js');

var _utilsCustomTimeRangesJs2 = _interopRequireDefault(_utilsCustomTimeRangesJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var _modelsVideoModelJs = _dereq_('./models/VideoModel.js');

var _modelsVideoModelJs2 = _interopRequireDefault(_modelsVideoModelJs);

var _TextTracksJs = _dereq_('./TextTracks.js');

var _TextTracksJs2 = _interopRequireDefault(_TextTracksJs);

var _codemIsoboxer = _dereq_('codem-isoboxer');

var _codemIsoboxer2 = _interopRequireDefault(_codemIsoboxer);

var _externalsCea608ParserJs = _dereq_('../../externals/cea608-parser.js');

var _externalsCea608ParserJs2 = _interopRequireDefault(_externalsCea608ParserJs);

function TextSourceBuffer() {

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var embeddedInitialized = false;
    var captionId = 0;

    var instance = undefined,
        boxParser = undefined,
        errHandler = undefined,
        adapter = undefined,
        dashManifestModel = undefined,
        mediaController = undefined,
        allTracksAreDisabled = undefined,
        parser = undefined,
        VTTParser = undefined,
        TTMLParser = undefined,
        fragmentedTextBoxParser = undefined,
        mediaInfos = undefined,
        textTracks = undefined,
        isFragmented = undefined,
        fragmentModel = undefined,
        initializationSegmentReceived = undefined,
        timescale = undefined,
        fragmentedTracks = undefined,
        videoModel = undefined,
        streamController = undefined,
        firstSubtitleStart = undefined,
        currFragmentedTrackIdx = undefined,
        embeddedTracks = undefined,
        embeddedInitializationSegmentReceived = undefined,
        embeddedTimescale = undefined,
        embeddedLastSequenceNumber = undefined,
        embeddedSequenceNumbers = undefined,
        embeddedCea608FieldParsers = undefined;

    function initialize(type, bufferController) {
        allTracksAreDisabled = false;
        parser = null;
        fragmentModel = null;
        initializationSegmentReceived = false;
        timescale = NaN;
        fragmentedTracks = [];
        firstSubtitleStart = null;

        if (!embeddedInitialized) {
            initEmbedded();
        }

        var streamProcessor = bufferController.getStreamProcessor();

        mediaInfos = streamProcessor.getMediaInfoArr();
        textTracks.setConfig({ videoModel: videoModel });
        textTracks.initialize();
        isFragmented = !dashManifestModel.getIsTextTrack(type);
        boxParser = (0, _utilsBoxParserJs2['default'])(context).getInstance();
        fragmentedTextBoxParser = (0, _dashUtilsFragmentedTextBoxParserJs2['default'])(context).getInstance();
        fragmentedTextBoxParser.setConfig({ boxParser: boxParser });

        if (isFragmented) {
            fragmentModel = streamProcessor.getFragmentModel();
            this.buffered = (0, _utilsCustomTimeRangesJs2['default'])(context).create();
            fragmentedTracks = mediaController.getTracksFor('fragmentedText', streamController.getActiveStreamInfo());
            var currFragTrack = mediaController.getCurrentTrackFor('fragmentedText', streamController.getActiveStreamInfo());
            for (var i = 0; i < fragmentedTracks.length; i++) {
                if (fragmentedTracks[i] === currFragTrack) {
                    currFragmentedTrackIdx = i;
                    break;
                }
            }
        }
    }

    function initEmbedded() {
        embeddedTracks = [];
        mediaInfos = [];
        videoModel = (0, _modelsVideoModelJs2['default'])(context).getInstance();
        textTracks = (0, _TextTracksJs2['default'])(context).getInstance();
        textTracks.setConfig({ videoModel: videoModel });
        textTracks.initialize();
        boxParser = (0, _utilsBoxParserJs2['default'])(context).getInstance();
        fragmentedTextBoxParser = (0, _dashUtilsFragmentedTextBoxParserJs2['default'])(context).getInstance();
        fragmentedTextBoxParser.setConfig({ boxParser: boxParser });
        isFragmented = false;
        currFragmentedTrackIdx = null;
        embeddedInitializationSegmentReceived = false;
        embeddedTimescale = 0;
        embeddedCea608FieldParsers = [];
        embeddedSequenceNumbers = [];
        embeddedLastSequenceNumber = null;
        embeddedInitialized = true;
    }

    function append(bytes, chunk) {
        var result, sampleList, i, samplesInfo, ccContent;
        var mediaInfo = chunk.mediaInfo;
        var mediaType = mediaInfo.type;
        var mimeType = mediaInfo.mimeType;
        var codecType = mediaInfo.codec || mimeType;
        if (!codecType) {
            log('No text type defined');
            return;
        }

        function createTextTrackFromMediaInfo(captionData, mediaInfo) {
            var textTrackInfo = new _voTextTrackInfoJs2['default']();
            var trackKindMap = { subtitle: 'subtitles', caption: 'captions' }; //Dash Spec has no "s" on end of KIND but HTML needs plural.
            var getKind = function getKind() {
                var kind = mediaInfo.roles.length > 0 ? trackKindMap[mediaInfo.roles[0]] : trackKindMap.caption;
                kind = kind === trackKindMap.caption || kind === trackKindMap.subtitle ? kind : trackKindMap.caption;
                return kind;
            };

            var checkTTML = function checkTTML() {
                var ttml = false;
                if (mediaInfo.codec && mediaInfo.codec.search('stpp') >= 0) {
                    ttml = true;
                }
                if (mediaInfo.mimeType && mediaInfo.mimeType.search('ttml') >= 0) {
                    ttml = true;
                }
                return ttml;
            };

            textTrackInfo.captionData = captionData;
            textTrackInfo.lang = mediaInfo.lang;
            textTrackInfo.label = mediaInfo.id; // AdaptationSet id (an unsigned int)
            textTrackInfo.index = mediaInfo.index; // AdaptationSet index in manifest
            textTrackInfo.isTTML = checkTTML();
            textTrackInfo.video = videoModel.getElement();
            textTrackInfo.defaultTrack = getIsDefault(mediaInfo);
            textTrackInfo.isFragmented = isFragmented;
            textTrackInfo.isEmbedded = mediaInfo.isEmbedded ? true : false;
            textTrackInfo.kind = getKind();
            var totalNrTracks = (mediaInfos ? mediaInfos.length : 0) + embeddedTracks.length;
            textTracks.addTextTrack(textTrackInfo, totalNrTracks);
        }

        if (mediaType === 'fragmentedText') {
            if (!initializationSegmentReceived) {
                initializationSegmentReceived = true;
                for (i = 0; i < mediaInfos.length; i++) {
                    createTextTrackFromMediaInfo(null, mediaInfos[i]);
                }
                timescale = fragmentedTextBoxParser.getMediaTimescaleFromMoov(bytes);
            } else {
                samplesInfo = fragmentedTextBoxParser.getSamplesInfo(bytes);
                sampleList = samplesInfo.sampleList;
                if (!firstSubtitleStart && sampleList.length > 0) {
                    firstSubtitleStart = sampleList[0].cts - chunk.start * timescale;
                }
                if (codecType.search('stpp') >= 0) {
                    parser = parser !== null ? parser : getParser(codecType);
                    for (i = 0; i < sampleList.length; i++) {
                        var _sample = sampleList[i];
                        var sampleStart = _sample.cts;
                        var sampleRelStart = sampleStart - firstSubtitleStart;
                        this.buffered.add(sampleRelStart / timescale, (sampleRelStart + _sample.duration) / timescale);
                        var dataView = new DataView(bytes, _sample.offset, _sample.size);
                        ccContent = _codemIsoboxer2['default'].Utils.dataViewToString(dataView, 'utf-8');
                        try {
                            result = parser.parse(ccContent, sampleStart / timescale, (sampleStart + _sample.duration) / timescale);
                            textTracks.addCaptions(currFragmentedTrackIdx, firstSubtitleStart / timescale, result);
                        } catch (e) {
                            log('TTML parser error: ' + e.message);
                        }
                    }
                } else {
                    // WebVTT case
                    var captionArray = [];
                    for (i = 0; i < sampleList.length; i++) {
                        var sample = sampleList[i];
                        sample.cts -= firstSubtitleStart;
                        this.buffered.add(sample.cts / timescale, (sample.cts + sample.duration) / timescale);
                        var sampleData = bytes.slice(sample.offset, sample.offset + sample.size);
                        // There are boxes inside the sampleData, so we need a ISOBoxer to get at it.
                        var sampleBoxes = _codemIsoboxer2['default'].parseBuffer(sampleData);

                        for (var j = 0; j < sampleBoxes.boxes.length; j++) {
                            var box1 = sampleBoxes.boxes[j];
                            log('VTT box1: ' + box1.type);
                            if (box1.type === 'vtte') {
                                continue; //Empty box
                            }
                            if (box1.type === 'vttc') {
                                log('VTT vttc boxes.length = ' + box1.boxes.length);
                                for (var k = 0; k < box1.boxes.length; k++) {
                                    var box2 = box1.boxes[k];
                                    log('VTT box2: ' + box2.type);
                                    if (box2.type === 'payl') {
                                        var cue_text = box2.cue_text;
                                        log('VTT cue_text = ' + cue_text);
                                        var start_time = sample.cts / timescale;
                                        var end_time = (sample.cts + sample.duration) / timescale;
                                        captionArray.push({
                                            start: start_time,
                                            end: end_time,
                                            data: cue_text,
                                            styles: {}
                                        });
                                        log('VTT ' + start_time + '-' + end_time + ' : ' + cue_text);
                                    }
                                }
                            }
                        }
                    }
                    if (captionArray.length > 0) {
                        textTracks.addCaptions(currFragmentedTrackIdx, 0, captionArray);
                    }
                }
            }
        } else if (mediaType === 'text') {
            var dataView = new DataView(bytes, 0, bytes.byteLength);
            ccContent = _codemIsoboxer2['default'].Utils.dataViewToString(dataView, 'utf-8');

            try {
                result = getParser(codecType).parse(ccContent);
                createTextTrackFromMediaInfo(result, mediaInfo);
            } catch (e) {
                errHandler.timedTextError(e, 'parse', ccContent);
            }
        } else if (mediaType === 'video') {
            //embedded text
            if (chunk.segmentType === 'InitializationSegment') {
                if (embeddedTimescale === 0) {
                    embeddedTimescale = fragmentedTextBoxParser.getMediaTimescaleFromMoov(bytes);
                    for (i = 0; i < embeddedTracks.length; i++) {
                        createTextTrackFromMediaInfo(null, embeddedTracks[i]);
                    }
                }
            } else {
                // MediaSegment
                if (embeddedTimescale === 0) {
                    log('CEA-608: No timescale for embeddedTextTrack yet');
                    return;
                }
                var makeCueAdderForIndex = function makeCueAdderForIndex(self, trackIndex) {
                    function newCue(startTime, endTime, captionScreen) {
                        var captionsArray = null;
                        if (videoModel.getTTMLRenderingDiv()) {
                            captionsArray = createHTMLCaptionsFromScreen(videoModel.getElement(), startTime, endTime, captionScreen);
                        } else {
                            var text = captionScreen.getDisplayText();
                            //log("CEA text: " + startTime + "-" + endTime + "  '" + text + "'");
                            captionsArray = [{ start: startTime, end: endTime, data: text, styles: {} }];
                        }
                        if (captionsArray) {
                            textTracks.addCaptions(trackIndex, 0, captionsArray);
                        }
                    }
                    return newCue;
                };

                samplesInfo = fragmentedTextBoxParser.getSamplesInfo(bytes);
                var sequenceNumber = samplesInfo.sequenceNumber;

                if (!embeddedCea608FieldParsers[0] && !embeddedCea608FieldParsers[1]) {
                    // Time to setup the CEA-608 parsing
                    var field = undefined,
                        handler = undefined,
                        trackIdx = undefined;
                    for (i = 0; i < embeddedTracks.length; i++) {
                        if (embeddedTracks[i].id === 'CC1') {
                            field = 0;
                            trackIdx = textTracks.getTrackIdxForId('CC1');
                        } else if (embeddedTracks[i].id === 'CC3') {
                            field = 1;
                            trackIdx = textTracks.getTrackIdxForId('CC3');
                        }
                        if (trackIdx === -1) {
                            log('CEA-608: data before track is ready.');
                            return;
                        }
                        handler = makeCueAdderForIndex(this, trackIdx);
                        embeddedCea608FieldParsers[i] = new _externalsCea608ParserJs2['default'].Cea608Parser(i, { 'newCue': handler }, null);
                    }
                }

                if (embeddedTimescale && embeddedSequenceNumbers.indexOf(sequenceNumber) == -1) {
                    if (embeddedLastSequenceNumber !== null && sequenceNumber !== embeddedLastSequenceNumber + 1) {
                        for (i = 0; i < embeddedCea608FieldParsers.length; i++) {
                            if (embeddedCea608FieldParsers[i]) {
                                embeddedCea608FieldParsers[i].reset();
                            }
                        }
                    }
                    var allCcData = extractCea608Data(bytes);

                    for (var fieldNr = 0; fieldNr < embeddedCea608FieldParsers.length; fieldNr++) {
                        var ccData = allCcData.fields[fieldNr];
                        var fieldParser = embeddedCea608FieldParsers[fieldNr];
                        if (fieldParser) {
                            /*if (ccData.length > 0 ) {
                                log("CEA-608 adding Data to field " + fieldNr + " " + ccData.length + "bytes");
                            }*/
                            for (i = 0; i < ccData.length; i++) {
                                fieldParser.addData(ccData[i][0] / embeddedTimescale, ccData[i][1]);
                            }
                            if (allCcData.endTime) {
                                fieldParser.cueSplitAtTime(allCcData.endTime / embeddedTimescale);
                            }
                        }
                    }
                    embeddedLastSequenceNumber = sequenceNumber;
                    embeddedSequenceNumbers.push(sequenceNumber);
                }
            }
        }
    }
    /**
     * Extract CEA-608 data from a buffer of data.
     * @parameter(data) ArrayBuffer of data
     * @returns ccData corresponding to one segment.
    */
    function extractCea608Data(data) {

        /** Insert [time, data] pairs in order into array. */
        var insertInOrder = function insertInOrder(arr, time, data) {
            var len = arr.length;
            if (len > 0) {
                if (time >= arr[len - 1][0]) {
                    arr.push([time, data]);
                } else {
                    for (var pos = len - 1; pos >= 0; pos--) {
                        if (time < arr[pos][0]) {
                            arr.splice(pos, 0, [time, data]);
                            break;
                        }
                    }
                }
            } else {
                arr.push([time, data]);
            }
        };

        var isoFile = boxParser.parse(data);
        var moof = isoFile.getBox('moof');
        var tfdt = isoFile.getBox('tfdt');
        //var tfhd = isoFile.getBox('tfhd'); //Can have a base_data_offset and other default values
        //log("tfhd: " + tfhd);
        //var saio = isoFile.getBox('saio'); // Offset possibly
        //var saiz = isoFile.getBox('saiz'); // Possible sizes
        var truns = isoFile.getBoxes('trun'); //
        var trun = null;

        if (truns.length === 0) {
            return null;
        }
        trun = truns[0];
        if (truns.length > 1) {
            log('Warning: Too many truns');
        }
        var baseOffset = moof.offset + trun.data_offset;
        //Doublecheck that trun.offset == moof.size + 8
        var sampleCount = trun.sample_count;
        var startPos = baseOffset;
        var baseSampleTime = tfdt.baseMediaDecodeTime;
        var raw = new DataView(data);
        var allCcData = { 'startTime': null, 'endTime': null, fields: [[], []] };
        var accDuration = 0;
        for (var i = 0; i < sampleCount; i++) {
            var sample = trun.samples[i];
            var sampleTime = baseSampleTime + accDuration + sample.sample_composition_time_offset;
            var cea608Ranges = _externalsCea608ParserJs2['default'].findCea608Nalus(raw, startPos, sample.sample_size);
            for (var j = 0; j < cea608Ranges.length; j++) {
                var ccData = _externalsCea608ParserJs2['default'].extractCea608DataFromRange(raw, cea608Ranges[j]);
                for (var k = 0; k < 2; k++) {
                    if (ccData[k].length > 0) {
                        insertInOrder(allCcData.fields[k], sampleTime, ccData[k]);
                    }
                }
            }

            accDuration += sample.sample_duration;
            startPos += sample.sample_size;
        }
        var endSampleTime = baseSampleTime + accDuration;
        allCcData.startTime = baseSampleTime;
        allCcData.endTime = endSampleTime;
        return allCcData;
    }

    /* HTML Rendering functions */
    function checkIndent(chars) {
        var line = '';

        for (var c = 0; c < chars.length; ++c) {
            var uc = chars[c];
            line += uc.uchar;
        }

        var l = line.length;
        var ll = line.replace(/^\s+/, '').length;
        return l - ll;
    }

    function getRegionProperties(region) {
        return 'left: ' + region.x * 3.125 + '%; top: ' + region.y1 * 6.66 + '%; width: ' + (100 - region.x * 3.125) + '%; height: ' + Math.max(region.y2 - 1 - region.y1, 1) * 6.66 + '%; align-items: flex-start; overflow: visible; -webkit-writing-mode: horizontal-tb;';
    }

    function createRGB(color) {
        if (color == 'red') {
            return 'rgb(255, 0, 0)';
        } else if (color == 'green') {
            return 'rgb(0, 255, 0)';
        } else if (color == 'blue') {
            return 'rgb(0, 0, 255)';
        } else if (color == 'cyan') {
            return 'rgb(0, 255, 255)';
        } else if (color == 'magenta') {
            return 'rgb(255, 0, 255)';
        } else if (color == 'yellow') {
            return 'rgb(255, 255, 0)';
        } else if (color == 'white') {
            return 'rgb(255, 255, 255)';
        } else if (color == 'black') {
            return 'rgb(0, 0, 0)';
        }
        return color;
    }

    function getStyle(videoElement, style) {
        var fontSize = videoElement.videoHeight / 15.0;
        if (style) {
            return 'font-size: ' + fontSize + 'px; font-family: Menlo, Consolas, \'Cutive Mono\', monospace; color: ' + (style.foreground ? createRGB(style.foreground) : 'rgb(255, 255, 255)') + '; font-style: ' + (style.italics ? 'italic' : 'normal') + '; text-decoration: ' + (style.underline ? 'underline' : 'none') + '; white-space: pre; background-color: ' + (style.background ? createRGB(style.background) : 'transparent') + ';';
        } else {
            return 'font-size: ' + fontSize + 'px; font-family: Menlo, Consolas, \'Cutive Mono\', monospace; justify-content: flex-start; text-align: left; color: rgb(255, 255, 255); font-style: normal; white-space: pre; line-height: normal; font-weight: normal; text-decoration: none; width: 100%; display: flex;';
        }
    }

    function ltrim(s) {
        var trimmed = s.replace(/^\s+/g, '');
        return trimmed;
    }
    function rtrim(s) {
        var trimmed = s.replace(/\s+$/g, '');
        return trimmed;
    }

    function createHTMLCaptionsFromScreen(videoElement, startTime, endTime, captionScreen) {

        var currRegion = null;
        var existingRegion = null;
        var lastRowHasText = false;
        var lastRowIndentL = -1;
        var currP = { start: startTime, end: endTime, spans: [] };
        var currentStyle = 'style_cea608_white_black';
        var seenRegions = {};
        var styleStates = {};
        var regions = [];
        var r = undefined,
            s = undefined;

        for (r = 0; r < 15; ++r) {
            var row = captionScreen.rows[r];
            var line = '';
            var prevPenState = null;

            if (false === row.isEmpty()) {
                /* Row is not empty */

                /* Get indentation of this row */
                var rowIndent = checkIndent(row.chars);

                /* Create a new region is there is none */
                if (currRegion === null) {
                    currRegion = { x: rowIndent, y1: r, y2: r + 1, p: [] };
                }

                /* Check if indentation has changed and we had text of last row */
                if (rowIndent !== lastRowIndentL && lastRowHasText) {
                    currRegion.p.push(currP);
                    currP = { start: startTime, end: endTime, spans: [] };
                    currRegion.y2 = r;
                    currRegion.name = 'region_' + currRegion.x + '_' + currRegion.y1 + '_' + currRegion.y2;
                    if (false === seenRegions.hasOwnProperty(currRegion.name)) {
                        regions.push(currRegion);
                        seenRegions[currRegion.name] = currRegion;
                    } else {
                        existingRegion = seenRegions[currRegion.name];
                        existingRegion.p.contat(currRegion.p);
                    }

                    currRegion = { x: rowIndent, y1: r, y2: r + 1, p: [] };
                }

                for (var c = 0; c < row.chars.length; ++c) {
                    var uc = row.chars[c];
                    var currPenState = uc.penState;
                    if (prevPenState === null || !currPenState.equals(prevPenState)) {
                        if (line.trim().length > 0) {
                            currP.spans.push({ name: currentStyle, line: line, row: r });
                            line = '';
                        }

                        var currPenStateString = 'style_cea608_' + currPenState.foreground + '_' + currPenState.background;
                        if (currPenState.underline) {
                            currPenStateString += '_underline';
                        }
                        if (currPenState.italics) {
                            currPenStateString += '_italics';
                        }

                        if (!styleStates.hasOwnProperty(currPenStateString)) {
                            styleStates[currPenStateString] = JSON.parse(JSON.stringify(currPenState));
                        }

                        prevPenState = currPenState;

                        currentStyle = currPenStateString;
                    }

                    line += uc.uchar;
                }

                if (line.trim().length > 0) {
                    currP.spans.push({ name: currentStyle, line: line, row: r });
                }

                lastRowHasText = true;
                lastRowIndentL = rowIndent;
            } else {
                /* Row is empty */
                lastRowHasText = false;
                lastRowIndentL = -1;

                if (currRegion) {
                    currRegion.p.push(currP);
                    currP = { start: startTime, end: endTime, spans: [] };
                    currRegion.y2 = r;
                    currRegion.name = 'region_' + currRegion.x + '_' + currRegion.y1 + '_' + currRegion.y2;
                    if (false === seenRegions.hasOwnProperty(currRegion.name)) {
                        regions.push(currRegion);
                        seenRegions[currRegion.name] = currRegion;
                    } else {
                        existingRegion = seenRegions[currRegion.name];
                        existingRegion.p.contat(currRegion.p);
                    }

                    currRegion = null;
                }
            }
        }

        if (currRegion) {
            currRegion.p.push(currP);
            currRegion.y2 = r + 1;
            currRegion.name = 'region_' + currRegion.x + '_' + currRegion.y1 + '_' + currRegion.y2;
            if (false === seenRegions.hasOwnProperty(currRegion.name)) {
                regions.push(currRegion);
                seenRegions[currRegion.name] = currRegion;
            } else {
                existingRegion = seenRegions[currRegion.name];
                existingRegion.p.contat(currRegion.p);
            }

            currRegion = null;
        }

        //log(styleStates);
        //log(regions);

        var captionsArray = [];

        /* Loop thru regions */
        for (r = 0; r < regions.length; ++r) {
            var region = regions[r];

            var cueID = 'sub_' + captionId++;
            var finalDiv = document.createElement('div');
            finalDiv.id = 'subtitle_' + cueID;
            var cueRegionProperties = getRegionProperties(region);
            finalDiv.style.cssText = 'position: absolute; margin: 0; display: flex; box-sizing: border-box; pointer-events: none;' + cueRegionProperties;

            var bodyDiv = document.createElement('div');
            bodyDiv.className = 'paragraph bodyStyle';
            bodyDiv.style.cssText = getStyle(videoElement);

            var cueUniWrapper = document.createElement('div');
            cueUniWrapper.className = 'cueUniWrapper';
            cueUniWrapper.style.cssText = 'unicode-bidi: normal; direction: ltr;';

            for (var p = 0; p < region.p.length; ++p) {
                var ptag = region.p[p];
                var lastSpanRow = 0;
                for (s = 0; s < ptag.spans.length; ++s) {
                    var span = ptag.spans[s];
                    if (span.line.length > 0) {
                        if (s !== 0 && lastSpanRow != span.row) {
                            var brElement = document.createElement('br');
                            brElement.className = 'lineBreak';
                            cueUniWrapper.appendChild(brElement);
                        }
                        var sameRow = false;
                        if (lastSpanRow === span.row) {
                            sameRow = true;
                        }
                        lastSpanRow = span.row;
                        var spanStyle = styleStates[span.name];
                        var spanElement = document.createElement('span');
                        spanElement.className = 'spanPadding ' + span.name + ' customSpanColor';
                        spanElement.style.cssText = getStyle(videoElement, spanStyle);
                        if (s !== 0 && sameRow) {
                            if (s === ptag.spans.length - 1) {
                                spanElement.textContent = rtrim(span.line);
                            } else {
                                spanElement.textContent = span.line;
                            }
                        } else {
                            if (s === 0) {
                                if (ptag.spans.length > 1) {
                                    /* Check if next text is on same row */
                                    if (span.row === ptag.spans[1].row) {
                                        /* Next element on same row, trim start */
                                        spanElement.textContent = ltrim(span.line);
                                    } else {
                                        /* Different rows, trim */
                                        spanElement.textContent = span.line.trim();
                                    }
                                } else {
                                    spanElement.textContent = span.line.trim();
                                }
                            } else {
                                spanElement.textContent = span.line.trim();
                            }
                        }
                        cueUniWrapper.appendChild(spanElement);
                    }
                }
            }

            bodyDiv.appendChild(cueUniWrapper);

            finalDiv.appendChild(bodyDiv);

            var fontSize = { 'bodyStyle': 90 };
            for (s in styleStates) {
                if (styleStates.hasOwnProperty(s)) {
                    fontSize[s] = 90;
                }
            }

            captionsArray.push({ type: 'html',
                start: startTime,
                end: endTime,
                cueHTMLElement: finalDiv,
                cueID: cueID,
                cellResolution: [32, 15],
                isFromCEA608: true,
                regions: regions,
                regionID: region.name,
                videoHeight: videoElement.videoHeight,
                videoWidth: videoElement.videoWidth,
                fontSize: fontSize || {
                    defaultFontSize: '100'
                },
                lineHeight: {},
                linePadding: {}
            });
        }
        return captionsArray;
    }

    function abort() {
        textTracks.deleteAllTextTracks();
        allTracksAreDisabled = false;
        parser = null;
        fragmentedTextBoxParser = null;
        mediaInfos = null;
        textTracks = null;
        isFragmented = false;
        fragmentModel = null;
        initializationSegmentReceived = false;
        timescale = NaN;
        fragmentedTracks = [];
        videoModel = null;
        streamController = null;
        embeddedInitialized = false;
        embeddedTracks = null;
    }

    function addEmbeddedTrack(mediaInfo) {
        if (!embeddedInitialized) {
            initEmbedded();
        }
        if (mediaInfo.id === 'CC1' || mediaInfo.id === 'CC3') {
            embeddedTracks.push(mediaInfo);
        } else {
            log('Warning: Embedded track ' + mediaInfo.id + ' not supported!');
        }
    }

    function resetEmbedded() {
        embeddedInitialized = false;
        embeddedTracks = [];
        embeddedCea608FieldParsers = [null, null];
        embeddedSequenceNumbers = [];
        embeddedLastSequenceNumber = null;
    }

    function getAllTracksAreDisabled() {
        return allTracksAreDisabled;
    }

    function setConfig(config) {
        if (!config) return;

        if (config.errHandler) {
            errHandler = config.errHandler;
        }
        if (config.adapter) {
            adapter = config.adapter;
        }
        if (config.dashManifestModel) {
            dashManifestModel = config.dashManifestModel;
        }
        if (config.mediaController) {
            mediaController = config.mediaController;
        }
        if (config.videoModel) {
            videoModel = config.videoModel;
        }
        if (config.streamController) {
            streamController = config.streamController;
        }
        if (config.textTracks) {
            textTracks = config.textTracks;
        }
        if (config.VTTParser) {
            VTTParser = config.VTTParser;
        }
        if (config.TTMLParser) {
            TTMLParser = config.TTMLParser;
        }
    }

    function setTextTrack() {

        var el = videoModel.getElement();
        var tracks = el.textTracks;
        var ln = tracks.length;
        var nrNonEmbeddedTracks = ln - embeddedTracks.length;
        var oldTrackIdx = textTracks.getCurrentTrackIdx();

        for (var i = 0; i < ln; i++) {
            var track = tracks[i];
            allTracksAreDisabled = track.mode !== 'showing';
            if (track.mode === 'showing') {
                if (oldTrackIdx !== i) {
                    // do not reset track if already the current track.  This happens when all captions get turned off via UI and then turned on again and with videojs.
                    textTracks.setCurrentTrackIdx(i);
                    textTracks.addCaptions(i, 0, null); // Make sure that previously queued captions are added as cues
                    if (isFragmented && i < nrNonEmbeddedTracks) {
                        var currentFragTrack = mediaController.getCurrentTrackFor('fragmentedText', streamController.getActiveStreamInfo());
                        var newFragTrack = fragmentedTracks[i];
                        if (newFragTrack !== currentFragTrack) {
                            fragmentModel.abortRequests();
                            textTracks.deleteTrackCues(currentFragTrack);
                            mediaController.setTrack(newFragTrack);
                            currFragmentedTrackIdx = i;
                        }
                    }
                }
                break;
            }
        }

        if (allTracksAreDisabled) {
            textTracks.setCurrentTrackIdx(-1);
        }
    }

    function getIsDefault(mediaInfo) {
        //TODO How to tag default. currently same order as listed in manifest.
        // Is there a way to mark a text adaptation set as the default one? DASHIF meeting talk about using role which is being used for track KIND
        // Eg subtitles etc. You can have multiple role tags per adaptation Not defined in the spec yet.
        var isDefault = false;
        if (embeddedTracks.length > 1) {
            isDefault = mediaInfo.id && mediaInfo.id === 'CC1'; // CC1 if both CC1 and CC3 exist
        } else if (embeddedTracks.length === 1) {
                if (mediaInfo.id && mediaInfo.id.substring(0, 2) === 'CC') {
                    // Either CC1 or CC3
                    isDefault = true;
                }
            } else {
                isDefault = mediaInfo.index === mediaInfos[0].index;
            }
        return isDefault;
    }

    function getParser(codecType) {
        var parser;
        if (codecType.search('vtt') >= 0) {
            parser = VTTParser;
        } else if (codecType.search('ttml') >= 0 || codecType.search('stpp') >= 0) {
            parser = TTMLParser;
            parser.setConfig({ videoModel: videoModel });
        }
        return parser;
    }

    instance = {
        initialize: initialize,
        append: append,
        abort: abort,
        getAllTracksAreDisabled: getAllTracksAreDisabled,
        setTextTrack: setTextTrack,
        setConfig: setConfig,
        addEmbeddedTrack: addEmbeddedTrack,
        resetEmbedded: resetEmbedded
    };

    return instance;
}

TextSourceBuffer.__dashjs_factory_name = 'TextSourceBuffer';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(TextSourceBuffer);
module.exports = exports['default'];

},{"../../externals/cea608-parser.js":1,"../core/Debug.js":5,"../core/FactoryMaker.js":7,"../dash/utils/FragmentedTextBoxParser.js":18,"./TextTracks.js":42,"./models/VideoModel.js":67,"./utils/BoxParser.js":86,"./utils/CustomTimeRanges.js":88,"./vo/TextTrackInfo.js":109,"codem-isoboxer":4}],42:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreEventBusJs = _dereq_('../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

function TextTracks() {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;

    var instance = undefined,
        Cue = undefined,
        videoModel = undefined,
        video = undefined,
        textTrackQueue = undefined,
        trackElementArr = undefined,
        currentTrackIdx = undefined,
        actualVideoLeft = undefined,
        actualVideoTop = undefined,
        actualVideoWidth = undefined,
        actualVideoHeight = undefined,
        captionContainer = undefined,
        videoSizeCheckInterval = undefined,
        isChrome = undefined,
        fullscreenAttribute = undefined,
        displayCCOnTop = undefined,
        topZIndex = undefined;

    function initialize() {
        Cue = window.VTTCue || window.TextTrackCue;
        textTrackQueue = [];
        trackElementArr = [];
        currentTrackIdx = -1;
        actualVideoLeft = 0;
        actualVideoTop = 0;
        actualVideoWidth = 0;
        actualVideoHeight = 0;
        captionContainer = null;
        videoSizeCheckInterval = null;
        displayCCOnTop = false;
        topZIndex = 2147483647;

        //TODO Check if IE has resolved issues: Then revert to not using the addTextTrack API for all browsers.
        // https://connect.microsoft.com/IE/feedbackdetail/view/1660701/text-tracks-do-not-fire-change-addtrack-or-removetrack-events
        // https://connect.microsoft.com/IE/feedback/details/1573380/htmltrackelement-track-addcue-throws-invalidstateerror-when-adding-new-cue
        // Same issue with Firefox.
        //isIE11orEdge = !!navigator.userAgent.match(/Trident.*rv[ :]*11\./) || navigator.userAgent.match(/Edge/);
        //isFirefox = !!navigator.userAgent.match(/Firefox/);
        isChrome = !!navigator.userAgent.match(/Chrome/) && !navigator.userAgent.match(/Edge/);
        if (document.fullscreenElement !== undefined) {
            fullscreenAttribute = 'fullscreenElement'; // Standard and Edge
        } else if (document.webkitIsFullScreen !== undefined) {
                fullscreenAttribute = 'webkitIsFullScreen'; // Chrome and Safari (and Edge)
            } else if (document.msFullscreenElement) {
                    // IE11
                    fullscreenAttribute = 'msFullscreenElement';
                } else if (document.mozFullScreen) {
                    // Firefox
                    fullscreenAttribute = 'mozFullScreen';
                }
    }

    function createTrackForUserAgent(i) {
        var kind = textTrackQueue[i].kind;
        var label = textTrackQueue[i].label !== undefined ? textTrackQueue[i].label : textTrackQueue[i].lang;
        var lang = textTrackQueue[i].lang;
        var track = isChrome ? document.createElement('track') : video.addTextTrack(kind, label, lang);

        if (isChrome) {
            track.kind = kind;
            track.label = label;
            track.srclang = lang;
        }

        return track;
    }

    function displayCConTop(value) {
        displayCCOnTop = value;
        if (!captionContainer || document[fullscreenAttribute]) return;
        captionContainer.style.zIndex = value ? topZIndex : null;
    }

    function addTextTrack(textTrackInfoVO, totalTextTracks) {

        if (textTrackQueue.length === totalTextTracks) {
            log('Trying to add too many tracks.');
            return;
        }

        textTrackQueue.push(textTrackInfoVO);
        if (video === undefined) {
            video = textTrackInfoVO.video;
        }

        if (textTrackQueue.length === totalTextTracks) {
            textTrackQueue.sort(function (a, b) {
                //Sort in same order as in manifest
                return a.index - b.index;
            });
            captionContainer = videoModel.getTTMLRenderingDiv();
            var defaultIndex = -1;
            for (var i = 0; i < textTrackQueue.length; i++) {
                var track = createTrackForUserAgent.call(this, i);
                trackElementArr.push(track); //used to remove tracks from video element when added manually

                if (textTrackQueue[i].defaultTrack) {
                    // track.default is an object property identifier that is a reserved word
                    // The following jshint directive is used to suppressed the warning "Expected an identifier and instead saw 'default' (a reserved word)"
                    /*jshint -W024 */
                    track['default'] = true;
                    defaultIndex = i;
                }
                if (isChrome) {
                    video.appendChild(track);
                }
                var textTrack = video.textTracks[i];
                textTrack.nonAddedCues = [];
                if (captionContainer && (textTrackQueue[i].isTTML || textTrackQueue[i].isEmbedded)) {
                    textTrack.renderingType = 'html';
                } else {
                    textTrack.renderingType = 'default';
                }
                this.addCaptions(i, 0, textTrackQueue[i].captionData);
                eventBus.trigger(_coreEventsEventsJs2['default'].TEXT_TRACK_ADDED);
            }
            setCurrentTrackIdx.call(this, defaultIndex);
            if (defaultIndex >= 0) {
                video.textTracks[defaultIndex].mode = 'showing';
                this.addCaptions(defaultIndex, 0, null);
            }
            eventBus.trigger(_coreEventsEventsJs2['default'].TEXT_TRACKS_ADDED, { index: currentTrackIdx, tracks: textTrackQueue }); //send default idx.
        }
    }

    function getVideoVisibleVideoSize(viewWidth, viewHeight, videoWidth, videoHeight, aspectRatio, use80Percent) {
        var viewAspectRatio = viewWidth / viewHeight;
        var videoAspectRatio = videoWidth / videoHeight;

        var videoPictureX = 0;
        var videoPictureY = 0;
        var videoPictureWidth = 0;
        var videoPictureHeight = 0;

        if (viewAspectRatio > videoAspectRatio) {
            videoPictureHeight = viewHeight;
            videoPictureWidth = videoPictureHeight / videoHeight * videoWidth;
            videoPictureX = (viewWidth - videoPictureWidth) / 2;
            videoPictureY = 0;
        } else {
            videoPictureWidth = viewWidth;
            videoPictureHeight = videoPictureWidth / videoWidth * videoHeight;
            videoPictureX = 0;
            videoPictureY = (viewHeight - videoPictureHeight) / 2;
        }

        var videoPictureXAspect = 0;
        var videoPictureYAspect = 0;
        var videoPictureWidthAspect = 0;
        var videoPictureHeightAspect = 0;
        var videoPictureAspect = videoPictureWidth / videoPictureHeight;

        if (videoPictureAspect > aspectRatio) {
            videoPictureHeightAspect = videoPictureHeight;
            videoPictureWidthAspect = videoPictureHeight / (1 / aspectRatio);
            videoPictureXAspect = (viewWidth - videoPictureWidthAspect) / 2;
            videoPictureYAspect = 0;
        } else {
            videoPictureWidthAspect = videoPictureWidth;
            videoPictureHeightAspect = videoPictureWidth / aspectRatio;
            videoPictureXAspect = 0;
            videoPictureYAspect = (viewHeight - videoPictureHeightAspect) / 2;
        }

        if (use80Percent) {
            return { x: videoPictureXAspect + videoPictureWidthAspect * 0.1,
                y: videoPictureYAspect + videoPictureHeightAspect * 0.1,
                w: videoPictureWidthAspect * 0.8,
                h: videoPictureHeightAspect * 0.8 }; /* Maximal picture size in videos aspect ratio */
        } else {
                return { x: videoPictureXAspect,
                    y: videoPictureYAspect,
                    w: videoPictureWidthAspect,
                    h: videoPictureHeightAspect }; /* Maximal picture size in videos aspect ratio */
            }
    }

    function checkVideoSize() {
        var track = this.getCurrentTextTrack();
        if (track && track.renderingType === 'html') {
            // Create aspect ratio from cellResolutions
            var aspectRatio = 1;
            if (track.cellResolution) {
                aspectRatio = track.cellResolution[0] / track.cellResolution[1];
            }
            var use80Percent = false;
            if (track.isFromCEA608) {
                // If this is CEA608 then use predefined aspect ratio
                aspectRatio = 3.5 / 3.0;
                use80Percent = true;
            }

            var realVideoSize = getVideoVisibleVideoSize.call(this, video.clientWidth, video.clientHeight, video.videoWidth, video.videoHeight, aspectRatio, use80Percent);

            var newVideoWidth = realVideoSize.w;
            var newVideoHeight = realVideoSize.h;

            if (newVideoWidth != actualVideoWidth || newVideoHeight != actualVideoHeight) {
                actualVideoLeft = realVideoSize.x;
                actualVideoTop = realVideoSize.y;
                actualVideoWidth = newVideoWidth;
                actualVideoHeight = newVideoHeight;
                captionContainer.style.left = actualVideoLeft + 'px';
                captionContainer.style.top = actualVideoTop + 'px';
                captionContainer.style.width = actualVideoWidth + 'px';
                captionContainer.style.height = actualVideoHeight + 'px';

                // Video view has changed size, so resize any active cues
                for (var i = 0; track.activeCues && i < track.activeCues.length; ++i) {
                    var cue = track.activeCues[i];
                    cue.scaleCue(cue);
                }

                if (fullscreenAttribute && document[fullscreenAttribute] || displayCCOnTop) {
                    captionContainer.style.zIndex = topZIndex;
                } else {
                    captionContainer.style.zIndex = null;
                }
            }
        }
    }

    function scaleCue(activeCue) {
        var videoWidth = actualVideoWidth;
        var videoHeight = actualVideoHeight;
        var key, replaceValue, elements;

        var cellUnit = [videoWidth / activeCue.cellResolution[0], videoHeight / activeCue.cellResolution[1]];

        if (activeCue.linePadding) {
            for (key in activeCue.linePadding) {
                if (activeCue.linePadding.hasOwnProperty(key)) {
                    var valueLinePadding = activeCue.linePadding[key];
                    replaceValue = (valueLinePadding * cellUnit[0]).toString();
                    // Compute the CellResolution unit in order to process properties using sizing (fontSize, linePadding, etc).
                    var elementsSpan = document.getElementsByClassName('spanPadding');
                    for (var i = 0; i < elementsSpan.length; i++) {
                        elementsSpan[i].style.cssText = elementsSpan[i].style.cssText.replace(/(padding-left\s*:\s*)[\d.,]+(?=\s*px)/gi, '$1' + replaceValue);
                        elementsSpan[i].style.cssText = elementsSpan[i].style.cssText.replace(/(padding-right\s*:\s*)[\d.,]+(?=\s*px)/gi, '$1' + replaceValue);
                    }
                }
            }
        }

        if (activeCue.fontSize) {
            for (key in activeCue.fontSize) {
                if (activeCue.fontSize.hasOwnProperty(key)) {
                    var valueFontSize = activeCue.fontSize[key] / 100;
                    replaceValue = (valueFontSize * cellUnit[1]).toString();

                    if (key !== 'defaultFontSize') {
                        elements = document.getElementsByClassName(key);
                    } else {
                        elements = document.getElementsByClassName('paragraph');
                    }

                    for (var j = 0; j < elements.length; j++) {
                        elements[j].style.cssText = elements[j].style.cssText.replace(/(font-size\s*:\s*)[\d.,]+(?=\s*px)/gi, '$1' + replaceValue);
                    }
                }
            }
        }

        if (activeCue.lineHeight) {
            for (key in activeCue.lineHeight) {
                if (activeCue.lineHeight.hasOwnProperty(key)) {
                    var valueLineHeight = activeCue.lineHeight[key] / 100;
                    replaceValue = (valueLineHeight * cellUnit[1]).toString();
                    elements = document.getElementsByClassName(key);
                    for (var k = 0; k < elements.length; k++) {
                        elements[k].style.cssText = elements[k].style.cssText.replace(/(line-height\s*:\s*)[\d.,]+(?=\s*px)/gi, '$1' + replaceValue);
                    }
                }
            }
        }
    }

    /**
    * Add captions to track, store for later adding, or add captions added before
    */
    function addCaptions(trackIdx, timeOffset, captionData) {
        var track = trackIdx >= 0 ? video.textTracks[trackIdx] : null;
        var self = this;

        if (!track) return;
        if (track.mode !== 'showing') {
            if (captionData && captionData.length > 0) {
                track.nonAddedCues = track.nonAddedCues.concat(captionData);
            }
            return;
        }

        if (!captionData) {
            captionData = track.nonAddedCues;
            track.nonAddedCues = [];
        }

        if (!captionData || captionData.length === 0) {
            return;
        }

        for (var item in captionData) {
            var cue;
            var currentItem = captionData[item];

            track.cellResolution = currentItem.cellResolution;
            track.isFromCEA608 = currentItem.isFromCEA608;

            if (!videoSizeCheckInterval && currentItem.type == 'html') {
                videoSizeCheckInterval = setInterval(checkVideoSize.bind(this), 500);
            }

            //image subtitle extracted from TTML
            if (currentItem.type == 'image') {
                cue = new Cue(currentItem.start - timeOffset, currentItem.end - timeOffset, '');
                cue.image = currentItem.data;
                cue.id = currentItem.id;
                cue.size = 0; //discard the native display for this subtitles
                cue.type = 'image'; // active image overlay
                cue.onenter = function () {
                    var img = new Image();
                    img.id = 'ttmlImage_' + this.id;
                    img.src = this.image;
                    img.className = 'cue-image';
                    if (captionContainer) {
                        captionContainer.appendChild(img);
                    } else {
                        video.parentNode.appendChild(img);
                    }
                };

                cue.onexit = function () {
                    var container, i, imgs;
                    if (captionContainer) {
                        container = captionContainer;
                    } else {
                        container = video.parentNode;
                    }
                    imgs = container.childNodes;
                    for (i = 0; i < imgs.length; i++) {
                        if (imgs[i].id == 'ttmlImage_' + this.id) {
                            container.removeChild(imgs[i]);
                        }
                    }
                };
            } else if (currentItem.type === 'html') {
                cue = new Cue(currentItem.start - timeOffset, currentItem.end - timeOffset, '');
                cue.cueHTMLElement = currentItem.cueHTMLElement;
                cue.regions = currentItem.regions;
                cue.regionID = currentItem.regionID;
                cue.cueID = currentItem.cueID;
                cue.videoWidth = currentItem.videoWidth;
                cue.videoHeight = currentItem.videoHeight;
                cue.cellResolution = currentItem.cellResolution;
                cue.fontSize = currentItem.fontSize;
                cue.lineHeight = currentItem.lineHeight;
                cue.linePadding = currentItem.linePadding;
                cue.scaleCue = scaleCue.bind(self);
                captionContainer.style.left = actualVideoLeft + 'px';
                captionContainer.style.top = actualVideoTop + 'px';
                captionContainer.style.width = actualVideoWidth + 'px';
                captionContainer.style.height = actualVideoHeight + 'px';

                cue.onenter = function () {
                    if (track.mode == 'showing') {
                        captionContainer.appendChild(this.cueHTMLElement);
                        scaleCue.call(self, this);
                    }
                };

                cue.onexit = function () {
                    var divs = captionContainer.childNodes;
                    for (var i = 0; i < divs.length; ++i) {
                        if (divs[i].id == 'subtitle_' + this.cueID) {
                            captionContainer.removeChild(divs[i]);
                        }
                    }
                };
            } else {
                cue = new Cue(currentItem.start - timeOffset, currentItem.end - timeOffset, currentItem.data);
                if (currentItem.styles) {
                    if (currentItem.styles.align !== undefined && cue.hasOwnProperty('align')) {
                        cue.align = currentItem.styles.align;
                    }
                    if (currentItem.styles.line !== undefined && cue.hasOwnProperty('line')) {
                        cue.line = currentItem.styles.line;
                    }
                    if (currentItem.styles.position !== undefined && cue.hasOwnProperty('position')) {
                        cue.position = currentItem.styles.position;
                    }
                    if (currentItem.styles.size !== undefined && cue.hasOwnProperty('size')) {
                        cue.size = currentItem.styles.size;
                    }
                }
            }

            track.addCue(cue);
        }
    }

    function getCurrentTextTrack() {
        return currentTrackIdx >= 0 ? video.textTracks[currentTrackIdx] : null;
    }

    function getCurrentTrackIdx() {
        return currentTrackIdx;
    }

    function getTrackIdxForId(trackId) {
        var idx = -1;
        for (var i = 0; i < video.textTracks.length; i++) {
            if (video.textTracks[i].label === trackId) {
                idx = i;
                break;
            }
        }
        return idx;
    }

    function setCurrentTrackIdx(idx) {
        currentTrackIdx = idx;
        clearCues.call(this);
        if (idx >= 0) {
            var track = video.textTracks[idx];
            if (track.renderingType === 'html') {
                setNativeCueStyle.call(this);
            } else {
                removeNativeCueStyle.call(this);
            }
        } else {
            removeNativeCueStyle.call(this);
        }
    }

    function getTextTrack(idx) {
        return video.textTracks[idx];
    }

    function deleteTrackCues(track) {
        if (track.cues) {
            var cues = track.cues;
            var lastIdx = cues.length - 1;

            for (var r = lastIdx; r >= 0; r--) {
                track.removeCue(cues[r]);
            }

            track.mode = 'disabled';
        }
    }

    function deleteAllTextTracks() {
        var ln = trackElementArr.length;
        for (var i = 0; i < ln; i++) {
            if (isChrome) {
                video.removeChild(trackElementArr[i]);
            } else {
                var track = getTextTrack.call(this, i);
                track.nonAddedCues = [];
                deleteTrackCues.call(this, track);
            }
        }
        trackElementArr = [];
        textTrackQueue = [];
        if (videoSizeCheckInterval) {
            clearInterval(videoSizeCheckInterval);
            videoSizeCheckInterval = null;
        }
        clearCues.call(this);
    }

    function deleteTextTrack(idx) {
        video.removeChild(trackElementArr[idx]);
        trackElementArr.splice(idx, 1);
    }

    /* Set native cue style to transparent background to avoid it being displayed. */
    function setNativeCueStyle() {
        if (!isChrome) return;
        var styleElement = document.getElementById('native-cue-style');
        if (styleElement) return; //Already set

        styleElement = document.createElement('style');
        styleElement.id = 'native-cue-style';
        document.head.appendChild(styleElement);
        var stylesheet = styleElement.sheet;
        if (video.id) {
            stylesheet.insertRule('#' + video.id + '::cue {background: transparent}', 0);
        } else if (video.classList.length !== 0) {
            stylesheet.insertRule('.' + video.className + '::cue {background: transparent}', 0);
        } else {
            stylesheet.insertRule('video::cue {background: transparent}', 0);
        }
    }

    /* Remove the extra cue style with transparent background for native cues. */
    function removeNativeCueStyle() {
        if (!isChrome) return;
        var styleElement = document.getElementById('native-cue-style');
        if (styleElement) {
            document.head.removeChild(styleElement);
        }
    }

    function clearCues() {
        if (captionContainer) {
            while (captionContainer.firstChild) {
                captionContainer.removeChild(captionContainer.firstChild);
            }
        }
    }

    function setConfig(config) {
        if (!config) return;

        if (config.videoModel) {
            videoModel = config.videoModel;
        }
    }

    instance = {
        initialize: initialize,
        displayCConTop: displayCConTop,
        addTextTrack: addTextTrack,
        addCaptions: addCaptions,
        getTextTrack: getTextTrack,
        getCurrentTextTrack: getCurrentTextTrack,
        getCurrentTrackIdx: getCurrentTrackIdx,
        setCurrentTrackIdx: setCurrentTrackIdx,
        getTrackIdxForId: getTrackIdxForId,
        deleteTrackCues: deleteTrackCues,
        deleteAllTextTracks: deleteAllTextTracks,
        deleteTextTrack: deleteTextTrack,
        setConfig: setConfig
    };

    return instance;
}

TextTracks.__dashjs_factory_name = 'TextTracks';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(TextTracks);
module.exports = exports['default'];

},{"../core/Debug.js":5,"../core/EventBus.js":6,"../core/FactoryMaker.js":7,"../core/events/Events.js":9}],43:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * Represents data structure to keep and drive {@link DataChunk}
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _controllersMediaControllerJs = _dereq_('./controllers/MediaController.js');

var _controllersMediaControllerJs2 = _interopRequireDefault(_controllersMediaControllerJs);

var _utilsCustomTimeRangesJs = _dereq_('./utils/CustomTimeRanges.js');

var _utilsCustomTimeRangesJs2 = _interopRequireDefault(_utilsCustomTimeRangesJs);

var _voMetricsHTTPRequestJs = _dereq_('./vo/metrics/HTTPRequest.js');

var _voMetricsHTTPRequestJs2 = _interopRequireDefault(_voMetricsHTTPRequestJs);

var _coreEventBusJs = _dereq_('../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function VirtualBuffer() {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        data = undefined,
        sourceBufferController = undefined;

    function setup() {
        data = {};
    }

    /**
     * Adds DataChunk to array of chunks
     * @param {@link DataChunk}
     * @memberof VirtualBuffer#
     */
    function append(chunk) {
        var streamId = chunk.streamId;
        var mediaType = chunk.mediaInfo.type;
        var segmentType = chunk.segmentType;
        var start = chunk.start;
        var end = chunk.end;

        data[streamId] = data[streamId] || createDataStorage();
        data[streamId][mediaType][segmentType].push(chunk);
        sortArrayByProperty(data[streamId][mediaType][segmentType], 'index');

        if (!isNaN(start) && !isNaN(end)) {
            data[streamId][mediaType].calculatedBufferedRanges.add(start, end);
            eventBus.trigger(_coreEventsEventsJs2['default'].CHUNK_APPENDED, { chunk: chunk, sender: this });
        }
    }

    /**
     * Adds DataChunk to array of appended chunks and updates virual ranges of appended chunks
     * @param {@link DataChunk}
     * @param buffer {SourceBuffer}
     * @memberof VirtualBuffer#
     */
    function storeAppendedChunk(chunk, buffer) {
        if (!chunk || !buffer) return;

        // after the media segment has bee appended we check how the buffered ranges of SourceBuffer have been change. The
        // difference is the actual buffered range of the appended segment.
        // We need to update actualBufferedRanges so that it reflects SourceBuffer ranges.
        // Also we store the appended chunk so that any BufferController has access to the list
        // of appended chunks.
        var streamId = chunk.streamId;
        var mediaType = chunk.mediaInfo.type;
        var bufferedRanges = data[streamId][mediaType].actualBufferedRanges;
        var oldChunk = getChunks({ streamId: streamId, mediaType: mediaType, appended: true, start: chunk.start })[0];

        var diff, idx;

        if (oldChunk) {
            idx = data[streamId][mediaType].appended.indexOf(oldChunk);
            data[streamId][mediaType].appended[idx] = chunk;
        } else {
            data[streamId][mediaType].appended.push(chunk);
        }

        sortArrayByProperty(data[streamId][mediaType].appended, 'start');
        diff = sourceBufferController.getRangeDifference(bufferedRanges, buffer);

        if (!diff) {
            if (oldChunk) {
                chunk.bufferedRange = oldChunk.bufferedRange;
            } else {
                //TODO this is dirty fix for a case when segments are not aligned across representations and thus oldChunk is not found
                // We should not use a calculated range here, only actual one should be used, but we don't know yet how to find it.
                chunk.bufferedRange = { start: chunk.start, end: chunk.end };
            }
            return;
        }

        chunk.bufferedRange = diff;
        bufferedRanges.add(diff.start, diff.end);

        if (!oldChunk) return;

        // if there is an old chunk already appended for the same index, we may need to adjust bufferedRange of a new chunk, because
        // it may be not valid.
        //
        // Example:
        //
        // Before appending the old chunk
        //
        // 0|-----Range-------|4
        // 0|--occupied space-|4
        //
        // After appending the old chunk
        //
        // 0|-----------------Range----------------|10
        // 0|-occupied space-4|-------Old chunk----|10
        //
        // After clearing the buffer from 7s to 10s
        //
        // 0|-----------------Range----|7
        // 0|-occupied space-4|OldChunk|7
        //
        // Since the old chunk has been cut only partially, after appending the new chunk its range will be detected as
        //
        // |-----------------Range-----------------|10
        // |----occupied space--------7|-New chunk-|10
        // This is not a valid value because the actual range of a new chunk is the same as the original range of
        // the old chunk, so we do the following adjustment
        chunk.bufferedRange.start = Math.min(oldChunk.bufferedRange.start, diff.start);
        chunk.bufferedRange.end = Math.max(oldChunk.bufferedRange.end, diff.end);
    }

    /**
     * Updates virual ranges of appended chunks according to the given ranges
     * @param filter
     * @param ranges
     * @memberof VirtualBuffer#
     */
    function updateBufferedRanges(filter, ranges) {
        if (!filter) return;

        var streamId = filter.streamId;
        var mediaType = filter.mediaType;
        var appendedChunks = getChunks({ streamId: streamId, mediaType: mediaType, appended: true });

        var remainingChunks = [];
        var start, end;

        data[streamId][mediaType].actualBufferedRanges = (0, _utilsCustomTimeRangesJs2['default'])(context).create();

        if (!ranges || ranges.length === 0) {
            data[streamId][mediaType].appended = [];
            return;
        }

        for (var i = 0, ln = ranges.length; i < ln; i++) {
            start = ranges.start(i);
            end = ranges.end(i);
            data[streamId][mediaType].actualBufferedRanges.add(start, end);
            // we need to select chunks that belong only to the new ranges
            remainingChunks = remainingChunks.concat(findChunksForRange(appendedChunks, { start: start, end: end }, true));
        }

        data[streamId][mediaType].appended = remainingChunks;
    }

    /**
     * Finds and returns {@link DataChunk} that satisfies filtering options
     * @param filter - an object that contains properties by which the method search for chunks
     * @returns {Array}
     * @memberof VirtualBuffer#
     */
    function getChunks(filter) {
        var originData = findData(filter);
        var segmentType = filter.segmentType;
        var appended = filter.appended;
        var removeOrigin = filter.removeOrigin;
        var limit = filter.limit || Number.POSITIVE_INFINITY;
        var mediaController = (0, _controllersMediaControllerJs2['default'])(context).getInstance();

        var ln = 0;
        var result = [];
        var sourceArr;

        if (!originData) return result;

        delete filter.streamId;
        delete filter.mediaType;
        delete filter.segmentType;
        delete filter.removeOrigin;
        delete filter.limit;
        delete filter.appended;

        sourceArr = appended ? originData.appended : segmentType ? originData[segmentType] : [];

        result = sourceArr.filter(function (item, idx, arr) {
            if (ln >= limit) return false;

            for (var prop in filter) {
                if (prop === 'mediaInfo') {
                    return mediaController.isTracksEqual(item[prop], filter[prop]);
                }

                if (filter.hasOwnProperty(prop) && item[prop] != filter[prop]) return false;
            }

            if (removeOrigin) {
                originData.calculatedBufferedRanges.remove(item.start, item.end);
                arr.splice(idx, 1);
            }

            ln++;

            return true;
        });

        if (filter.forRange) {
            result = findChunksForRange(result, filter.forRange, false);
        }

        return result;
    }

    /**
     * Finds and returns {@link DataChunk} that satisfies filtering options. Filtered chunks are removed
     * from the original array
     * @param filter - an object that contains properties by which the method search for chunks
     * @returns {Array}
     * @memberof VirtualBuffer#
     */
    function extract(filter) {
        filter.removeOrigin = true;
        return getChunks(filter);
    }

    /**
     * Calculates total buffer size across all Periods
     * @param {@link MediaInfo}
     * @returns {Number}
     * @memberof VirtualBuffer#
     */
    function getTotalBufferLevel(mediaInfo) {
        var mediaType = mediaInfo.type;
        var level = 0;

        for (var streamId in data) {
            if (data.hasOwnProperty(streamId)) {
                level += sourceBufferController.getTotalBufferedTime({ buffered: data[streamId][mediaType].calculatedBufferedRanges });
            }
        }

        return level;
    }

    function setConfig(config) {
        if (!config) return;

        if (config.sourceBufferController) {
            sourceBufferController = config.sourceBufferController;
        }
    }

    /**
     * @memberof VirtualBuffer#
     */
    function reset() {
        data = {};
    }

    function sortArrayByProperty(array, sortProp) {
        var compare = function compare(obj1, obj2) {
            if (obj1[sortProp] < obj2[sortProp]) return -1;
            if (obj1[sortProp] > obj2[sortProp]) return 1;
            return 0;
        };

        array.sort(compare);
    }

    function findData(filter) {
        var streamId = filter.streamId;
        var mediaType = filter.mediaType;

        if (!data[streamId]) return null;

        return data[streamId][mediaType];
    }

    function findChunksForRange(chunks, range, truncateChunk) {
        var rangeStart = range.start;
        var rangeEnd = range.end;
        var chunksForRange = [];

        var chunkStart, chunkEnd, isStartIncluded, isEndIncluded;

        chunks.forEach(function (chunk) {
            chunkStart = chunk.bufferedRange.start;
            chunkEnd = chunk.bufferedRange.end;
            isStartIncluded = chunkStart >= rangeStart && chunkStart < rangeEnd;
            isEndIncluded = chunkEnd > rangeStart && chunkEnd <= rangeEnd;

            // if a segment has been partially removed from SourceBuffer we select it as well, but we
            // need to update its bufferedRange
            //
            // Example 1:
            // |-----------------Range----------------|
            //                            |----Chunk-----|
            // becomes
            // |-----------------Range----------------|
            //                            |----Chunk--|
            // Example 2:
            //       |-----------------Range----------------|
            // |-------Chunk-----|
            // becomes
            //       |-----------------Range----------------|
            //       |-Chunk-----|

            if (isStartIncluded || isEndIncluded) {
                chunksForRange.push(chunk);

                if (truncateChunk) {
                    chunk.bufferedRange.start = isStartIncluded ? chunkStart : rangeStart;
                    chunk.bufferedRange.end = isEndIncluded ? chunkEnd : rangeEnd;
                }
            }
        });

        return chunksForRange;
    }

    function createDataStorage() {
        var data = {};

        data.audio = { calculatedBufferedRanges: (0, _utilsCustomTimeRangesJs2['default'])(context).create(),
            actualBufferedRanges: (0, _utilsCustomTimeRangesJs2['default'])(context).create(),
            appended: [] };
        data.audio[_voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE] = [];
        data.audio[_voMetricsHTTPRequestJs2['default'].INIT_SEGMENT_TYPE] = [];
        data.video = { calculatedBufferedRanges: (0, _utilsCustomTimeRangesJs2['default'])(context).create(),
            actualBufferedRanges: (0, _utilsCustomTimeRangesJs2['default'])(context).create(),
            appended: [] };
        data.video[_voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE] = [];
        data.video[_voMetricsHTTPRequestJs2['default'].INIT_SEGMENT_TYPE] = [];
        data.fragmentedText = { calculatedBufferedRanges: (0, _utilsCustomTimeRangesJs2['default'])(context).create(),
            actualBufferedRanges: (0, _utilsCustomTimeRangesJs2['default'])(context).create(),
            appended: [] };
        data.fragmentedText[_voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE] = [];
        data.fragmentedText[_voMetricsHTTPRequestJs2['default'].INIT_SEGMENT_TYPE] = [];

        return data;
    }

    instance = {
        append: append,
        extract: extract,
        getChunks: getChunks,
        storeAppendedChunk: storeAppendedChunk,
        updateBufferedRanges: updateBufferedRanges,
        getTotalBufferLevel: getTotalBufferLevel,
        setConfig: setConfig,
        reset: reset
    };

    setup();
    return instance;
}
VirtualBuffer.__dashjs_factory_name = 'VirtualBuffer';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(VirtualBuffer);
module.exports = exports['default'];

},{"../core/EventBus.js":6,"../core/FactoryMaker.js":7,"../core/events/Events.js":9,"./controllers/MediaController.js":52,"./utils/CustomTimeRanges.js":88,"./vo/metrics/HTTPRequest.js":117}],44:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var _voMetricsHTTPRequestJs = _dereq_('./vo/metrics/HTTPRequest.js');

var _voMetricsHTTPRequestJs2 = _interopRequireDefault(_voMetricsHTTPRequestJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _modelsMediaPlayerModelJs = _dereq_('./models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

/**
 * @Module XHRLoader
 * @description Manages download of resources via HTTP.
 */
function XHRLoader(cfg) {
    var context = this.context;

    //const log = Debug(context).getInstance().log;
    var mediaPlayerModel = (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance();

    var errHandler = cfg.errHandler;
    var metricsModel = cfg.metricsModel;
    var requestModifier = cfg.requestModifier;

    var instance = undefined;
    var xhrs = undefined;
    var delayedXhrs = undefined;
    var retryTimers = undefined;
    var downloadErrorToRequestTypeMap = undefined;

    function setup() {
        var _downloadErrorToRequestTypeMap;

        xhrs = [];
        delayedXhrs = [];
        retryTimers = [];

        downloadErrorToRequestTypeMap = (_downloadErrorToRequestTypeMap = {}, _defineProperty(_downloadErrorToRequestTypeMap, _voMetricsHTTPRequestJs2['default'].MPD_TYPE, errHandler.DOWNLOAD_ERROR_ID_MANIFEST), _defineProperty(_downloadErrorToRequestTypeMap, _voMetricsHTTPRequestJs2['default'].XLINK_EXPANSION_TYPE, errHandler.DOWNLOAD_ERROR_ID_XLINK), _defineProperty(_downloadErrorToRequestTypeMap, _voMetricsHTTPRequestJs2['default'].INIT_SEGMENT_TYPE, errHandler.DOWNLOAD_ERROR_ID_CONTENT), _defineProperty(_downloadErrorToRequestTypeMap, _voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE, errHandler.DOWNLOAD_ERROR_ID_CONTENT), _defineProperty(_downloadErrorToRequestTypeMap, _voMetricsHTTPRequestJs2['default'].INDEX_SEGMENT_TYPE, errHandler.DOWNLOAD_ERROR_ID_CONTENT), _defineProperty(_downloadErrorToRequestTypeMap, _voMetricsHTTPRequestJs2['default'].BITSTREAM_SWITCHING_SEGMENT_TYPE, errHandler.DOWNLOAD_ERROR_ID_CONTENT), _defineProperty(_downloadErrorToRequestTypeMap, _voMetricsHTTPRequestJs2['default'].OTHER_TYPE, errHandler.DOWNLOAD_ERROR_ID_CONTENT), _downloadErrorToRequestTypeMap);
    }

    function internalLoad(config, remainingAttempts) {

        var request = config.request;
        var xhr = ['audio', 'video', 'muxed'].indexOf(request.mediaType) > -1 && window.hola_cdn && window.hola_cdn.api && window.hola_cdn.api.new_http_request && window.hola_cdn.api.new_http_request(request) || new XMLHttpRequest();
        var traces = [];
        var firstProgress = true;
        var needFailureReport = true;
        var requestStartTime = new Date();
        var lastTraceTime = requestStartTime;
        var lastTraceReceivedCount = 0;

        var handleLoaded = function handleLoaded(success) {
            needFailureReport = false;

            request.requestStartDate = requestStartTime;
            request.requestEndDate = new Date();
            request.firstByteDate = request.firstByteDate || requestStartTime;

            if (!request.checkExistenceOnly) {
                metricsModel.addHttpRequest(request.mediaType, null, request.type, request.url, xhr.responseURL || null, request.serviceLocation || null, request.range || null, request.requestStartDate, request.firstByteDate, request.requestEndDate, xhr.status, request.duration, xhr.getAllResponseHeaders(), success ? traces : null);
            }
        };

        var onloadend = function onloadend() {
            if (xhrs.indexOf(xhr) === -1) {
                return;
            } else {
                xhrs.splice(xhrs.indexOf(xhr), 1);
            }

            if (needFailureReport) {
                handleLoaded(false);

                if (remainingAttempts > 0) {
                    remainingAttempts--;
                    retryTimers.push(setTimeout(function () {
                        internalLoad(config, remainingAttempts);
                    }, mediaPlayerModel.getRetryIntervalForType(request.type)));
                } else {
                    errHandler.downloadError(downloadErrorToRequestTypeMap[request.type], request.url, request);

                    if (config.error) {
                        config.error(request, 'error', xhr.statusText);
                    }

                    if (config.complete) {
                        config.complete(request, xhr.statusText);
                    }
                }
            }
        };

        var progress = function progress(event) {
            var currentTime = new Date();

            if (firstProgress) {
                firstProgress = false;
                if (!event.lengthComputable || event.lengthComputable && event.total !== event.loaded) {
                    request.firstByteDate = currentTime;
                }
            }

            if (event.lengthComputable) {
                request.bytesLoaded = event.loaded;
                request.bytesTotal = event.total;
            }

            traces.push({
                s: lastTraceTime,
                d: currentTime.getTime() - lastTraceTime.getTime(),
                b: [event.loaded ? event.loaded - lastTraceReceivedCount : 0]
            });

            lastTraceTime = currentTime;
            lastTraceReceivedCount = event.loaded;

            if (config.progress) {
                config.progress();
            }
        };

        var onload = function onload() {
            if (xhr.status >= 200 && xhr.status <= 299) {
                handleLoaded(true);

                if (config.success) {
                    config.success(xhr.response, xhr.statusText, request);
                }

                if (config.complete) {
                    config.complete(request, xhr.statusText);
                }
            }
        };

        try {
            var modifiedUrl = requestModifier.modifyRequestURL(request.url);
            var verb = request.checkExistenceOnly ? 'HEAD' : 'GET';

            xhr.open(verb, modifiedUrl, true);

            if (request.responseType) {
                xhr.responseType = request.responseType;
            }

            if (request.range) {
                xhr.setRequestHeader('Range', 'bytes=' + request.range);
            }

            if (!request.requestStartDate) {
                request.requestStartDate = requestStartTime;
            }

            xhr = requestModifier.modifyRequestHeader(xhr);

            xhr.onload = onload;
            xhr.onloadend = onloadend;
            xhr.onerror = onloadend;
            xhr.onprogress = progress;

            // Adds the ability to delay single fragment loading time to control buffer.
            var now = new Date().getTime();
            if (isNaN(request.delayLoadingTime) || now >= request.delayLoadingTime) {
                // no delay - just send xhr

                xhrs.push(xhr);
                xhr.send();
            } else {
                (function () {
                    // delay
                    var delayedXhr = { xhr: xhr };
                    delayedXhrs.push(delayedXhr);
                    delayedXhr.delayTimeout = setTimeout(function () {
                        if (delayedXhrs.indexOf(delayedXhr) === -1) {
                            return;
                        } else {
                            delayedXhrs.splice(delayedXhrs.indexOf(delayedXhr), 1);
                        }
                        try {
                            xhrs.push(delayedXhr.xhr);
                            delayedXhr.xhr.send();
                        } catch (e) {
                            delayedXhr.xhr.onerror();
                        }
                    }, request.delayLoadingTime - now);
                })();
            }
        } catch (e) {
            xhr.onerror();
        }
    }

    /**
     * Initiates a download of the resource described by config.request
     * @param {Object} config - contains request (FragmentRequest or derived type), and callbacks
     * @memberof module:XHRLoader
     * @instance
     */
    function load(config) {
        if (config.request) {
            internalLoad(config, mediaPlayerModel.getRetryAttemptsForType(config.request.type));
        }
    }

    /**
     * Aborts any inflight downloads
     * @memberof module:XHRLoader
     * @instance
     */
    function abort() {
        retryTimers.forEach(function (t) {
            return clearTimeout(t);
        });
        retryTimers = [];

        delayedXhrs.forEach(function (x) {
            return clearTimeout(x.delayTimeout);
        });
        delayedXhrs = [];

        xhrs.forEach(function (x) {
            // abort will trigger onloadend which we don't want
            // when deliberately aborting inflight requests -
            // set them to undefined so they are not called
            x.onloadend = x.onerror = undefined;
            x.abort();
        });
        xhrs = [];
    }

    instance = {
        load: load,
        abort: abort
    };

    setup();

    return instance;
}

XHRLoader.__dashjs_factory_name = 'XHRLoader';

var factory = _coreFactoryMakerJs2['default'].getClassFactory(XHRLoader);
exports['default'] = factory;
module.exports = exports['default'];

},{"../core/FactoryMaker.js":7,"./models/MediaPlayerModel.js":64,"./vo/metrics/HTTPRequest.js":117}],45:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _voErrorJs = _dereq_('./vo/Error.js');

var _voErrorJs2 = _interopRequireDefault(_voErrorJs);

var _XHRLoaderJs = _dereq_('./XHRLoader.js');

var _XHRLoaderJs2 = _interopRequireDefault(_XHRLoaderJs);

var _voMetricsHTTPRequestJs = _dereq_('./vo/metrics/HTTPRequest.js');

var _voMetricsHTTPRequestJs2 = _interopRequireDefault(_voMetricsHTTPRequestJs);

var _voTextRequestJs = _dereq_('./vo/TextRequest.js');

var _voTextRequestJs2 = _interopRequireDefault(_voTextRequestJs);

var _coreEventBusJs = _dereq_('../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var XLINK_LOADER_ERROR_LOADING_FAILURE = 1;

function XlinkLoader(config) {

    var RESOLVE_TO_ZERO = 'urn:mpeg:dash:resolve-to-zero:2013';

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var xhrLoader = (0, _XHRLoaderJs2['default'])(context).create({
        errHandler: config.errHandler,
        metricsModel: config.metricsModel,
        requestModifier: config.requestModifier
    });

    var instance = undefined;

    function load(url, element, resolveObject) {
        var report = function report(content, resolveToZero) {
            element.resolved = true;
            element.resolvedContent = content ? content : null;

            eventBus.trigger(_coreEventsEventsJs2['default'].XLINK_ELEMENT_LOADED, {
                element: element,
                resolveObject: resolveObject,
                error: content || resolveToZero ? null : new _voErrorJs2['default'](XLINK_LOADER_ERROR_LOADING_FAILURE, 'Failed loading Xlink element: ' + url)
            });
        };

        if (url === RESOLVE_TO_ZERO) {
            report(null, true);
        } else {
            var request = new _voTextRequestJs2['default'](url, _voMetricsHTTPRequestJs2['default'].XLINK_TYPE);

            xhrLoader.load({
                request: request,
                success: function success(data) {
                    report(data);
                },
                error: function error() {
                    report(null);
                }
            });
        }
    }

    function reset() {
        if (xhrLoader) {
            xhrLoader.abort();
            xhrLoader = null;
        }
    }

    instance = {
        load: load,
        reset: reset
    };

    return instance;
}

XlinkLoader.__dashjs_factory_name = 'XlinkLoader';

var factory = _coreFactoryMakerJs2['default'].getClassFactory(XlinkLoader);
factory.XLINK_LOADER_ERROR_LOADING_FAILURE = XLINK_LOADER_ERROR_LOADING_FAILURE;
exports['default'] = factory;
module.exports = exports['default'];

},{"../core/EventBus.js":6,"../core/FactoryMaker.js":7,"../core/events/Events.js":9,"./XHRLoader.js":44,"./vo/Error.js":100,"./vo/TextRequest.js":108,"./vo/metrics/HTTPRequest.js":117}],46:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _rulesSwitchRequest = _dereq_('../rules/SwitchRequest');

var _rulesSwitchRequest2 = _interopRequireDefault(_rulesSwitchRequest);

var _voBitrateInfoJs = _dereq_('../vo/BitrateInfo.js');

var _voBitrateInfoJs2 = _interopRequireDefault(_voBitrateInfoJs);

var _utilsDOMStorageJs = _dereq_('../utils/DOMStorage.js');

var _utilsDOMStorageJs2 = _interopRequireDefault(_utilsDOMStorageJs);

var _rulesAbrABRRulesCollectionJs = _dereq_('../rules/abr/ABRRulesCollection.js');

var _rulesAbrABRRulesCollectionJs2 = _interopRequireDefault(_rulesAbrABRRulesCollectionJs);

var _modelsMediaPlayerModelJs = _dereq_('../models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

var _modelsFragmentModelJs = _dereq_('../models/FragmentModel.js');

var _modelsFragmentModelJs2 = _interopRequireDefault(_modelsFragmentModelJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _modelsManifestModelJs = _dereq_('../models/ManifestModel.js');

var _modelsManifestModelJs2 = _interopRequireDefault(_modelsManifestModelJs);

var _dashModelsDashManifestModelJs = _dereq_('../../dash/models/DashManifestModel.js');

var _dashModelsDashManifestModelJs2 = _interopRequireDefault(_dashModelsDashManifestModelJs);

var _modelsVideoModelJs = _dereq_('../models/VideoModel.js');

var _modelsVideoModelJs2 = _interopRequireDefault(_modelsVideoModelJs);

var ABANDON_LOAD = 'abandonload';
var ALLOW_LOAD = 'allowload';
var DEFAULT_VIDEO_BITRATE = 1000;
var DEFAULT_AUDIO_BITRATE = 100;

function AbrController() {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        abrRulesCollection = undefined,
        rulesController = undefined,
        streamController = undefined,
        autoSwitchBitrate = undefined,
        topQualities = undefined,
        qualityDict = undefined,
        confidenceDict = undefined,
        bitrateDict = undefined,
        ratioDict = undefined,
        averageThroughputDict = undefined,
        streamProcessorDict = undefined,
        abandonmentStateDict = undefined,
        abandonmentTimeout = undefined,
        limitBitrateByPortal = undefined,
        manifestModel = undefined,
        dashManifestModel = undefined,
        videoModel = undefined,
        mediaPlayerModel = undefined,
        domStorage = undefined;

    function setup() {
        autoSwitchBitrate = { video: true, audio: true };
        topQualities = {};
        qualityDict = {};
        confidenceDict = {};
        bitrateDict = {};
        ratioDict = {};
        averageThroughputDict = {};
        abandonmentStateDict = {};
        streamProcessorDict = {};
        limitBitrateByPortal = false;
        domStorage = (0, _utilsDOMStorageJs2['default'])(context).getInstance();
        mediaPlayerModel = (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance();
        manifestModel = (0, _modelsManifestModelJs2['default'])(context).getInstance();
        dashManifestModel = (0, _dashModelsDashManifestModelJs2['default'])(context).getInstance();
        videoModel = (0, _modelsVideoModelJs2['default'])(context).getInstance();
    }

    function initialize(type, streamProcessor) {
        streamProcessorDict[type] = streamProcessor;
        abandonmentStateDict[type] = abandonmentStateDict[type] || {};
        abandonmentStateDict[type].state = ALLOW_LOAD;
        eventBus.on(_coreEventsEventsJs2['default'].LOADING_PROGRESS, onFragmentLoadProgress, this);
    }

    function setConfig(config) {
        if (!config) return;

        if (config.abrRulesCollection) {
            abrRulesCollection = config.abrRulesCollection;
        }
        if (config.rulesController) {
            rulesController = config.rulesController;
        }
        if (config.streamController) {
            streamController = config.streamController;
        }
    }

    function getTopQualityIndexFor(type, id) {
        var idx;
        topQualities[id] = topQualities[id] || {};

        if (!topQualities[id].hasOwnProperty(type)) {
            topQualities[id][type] = 0;
        }

        idx = checkMaxBitrate(topQualities[id][type], type);
        idx = checkMaxRepresentationRatio(idx, type, topQualities[id][type]);
        idx = checkPortalSize(idx, type);
        return idx;
    }

    /**
     * @param type
     * @returns {number} A value of the initial bitrate, kbps
     * @memberof AbrController#
     */
    function getInitialBitrateFor(type) {

        var savedBitrate = domStorage.getSavedBitrateSettings(type);

        if (!bitrateDict.hasOwnProperty(type)) {
            if (ratioDict.hasOwnProperty(type)) {
                var manifest = manifestModel.getValue();
                var representation = dashManifestModel.getAdaptationForType(manifest, 0, type).Representation;

                if (Array.isArray(representation)) {
                    bitrateDict[type] = representation[Math.round(representation.length * ratioDict[type]) - 1].bandwidth;
                } else {
                    bitrateDict[type] = 0;
                }
            } else if (!isNaN(savedBitrate)) {
                bitrateDict[type] = savedBitrate;
            } else {
                bitrateDict[type] = type === 'video' ? DEFAULT_VIDEO_BITRATE : DEFAULT_AUDIO_BITRATE;
            }
        }

        return bitrateDict[type];
    }

    /**
     * @param type
     * @param {number} value A value of the initial bitrate, kbps
     * @memberof AbrController#
     */
    function setInitialBitrateFor(type, value) {
        bitrateDict[type] = value;
    }

    function getInitialRepresentationRatioFor(type) {
        if (!ratioDict.hasOwnProperty(type)) {
            return null;
        }

        return ratioDict[type];
    }

    function setInitialRepresentationRatioFor(type, value) {
        ratioDict[type] = value;
    }

    function getMaxAllowedBitrateFor(type) {
        if (bitrateDict.hasOwnProperty('max') && bitrateDict.max.hasOwnProperty(type)) {
            return bitrateDict.max[type];
        }
        return NaN;
    }

    //TODO  change bitrateDict structure to hold one object for video and audio with initial and max values internal.
    // This means you need to update all the logic around initial bitrate DOMStorage, RebController etc...
    function setMaxAllowedBitrateFor(type, value) {
        bitrateDict.max = bitrateDict.max || {};
        bitrateDict.max[type] = value;
    }

    function getMaxAllowedRepresentationRatioFor(type) {
        if (ratioDict.hasOwnProperty('max') && ratioDict.max.hasOwnProperty(type)) {
            return ratioDict.max[type];
        }
        return 1;
    }

    function setMaxAllowedRepresentationRatioFor(type, value) {
        ratioDict.max = ratioDict.max || {};
        ratioDict.max[type] = value;
    }

    function getAutoSwitchBitrateFor(type) {
        return autoSwitchBitrate[type];
    }

    function setAutoSwitchBitrateFor(type, value) {
        autoSwitchBitrate[type] = value;
    }

    function getLimitBitrateByPortal() {
        return limitBitrateByPortal;
    }

    function setLimitBitrateByPortal(value) {
        limitBitrateByPortal = value;
    }

    function getPlaybackQuality(streamProcessor, completedCallback) {
        var type = streamProcessor.getType();
        var streamInfo = streamProcessor.getStreamInfo();
        var streamId = streamInfo.id;
        var quality, oldQuality, rules, confidence;
        var callback = function callback(res) {
            var topQualityIdx = getTopQualityIndexFor(type, streamId);

            quality = res.value;
            confidence = res.confidence;

            // be sure the quality valid!
            if (quality < 0) {
                quality = 0;
            }
            // zero based
            if (quality > topQualityIdx) {
                quality = topQualityIdx;
            }

            oldQuality = getQualityFor(type, streamInfo);
            if (quality !== oldQuality && (abandonmentStateDict[type].state === ALLOW_LOAD || quality > oldQuality)) {
                setInternalQuality(type, streamId, quality);
                setConfidenceFor(type, streamId, confidence);
                eventBus.trigger(_coreEventsEventsJs2['default'].QUALITY_CHANGED, { mediaType: type, streamInfo: streamProcessor.getStreamInfo(), oldQuality: oldQuality, newQuality: quality });
            }
            if (completedCallback) {
                completedCallback();
            }
        };

        quality = getQualityFor(type, streamInfo);
        confidence = getConfidenceFor(type, streamId);

        //log("ABR enabled? (" + autoSwitchBitrate + ")");
        if (!getAutoSwitchBitrateFor(type)) {
            if (completedCallback) {
                completedCallback();
            }
        } else {
            rules = abrRulesCollection.getRules(_rulesAbrABRRulesCollectionJs2['default'].QUALITY_SWITCH_RULES);
            rulesController.applyRules(rules, streamProcessor, callback, quality, function (currentValue, newValue) {
                currentValue = currentValue === _rulesSwitchRequest2['default'].NO_CHANGE ? 0 : currentValue;
                return Math.max(currentValue, newValue);
            });
        }
    }

    function setPlaybackQuality(type, streamInfo, newPlaybackQuality) {
        var id = streamInfo.id;
        var quality = getQualityFor(type, streamInfo);
        var isInt = newPlaybackQuality !== null && !isNaN(newPlaybackQuality) && newPlaybackQuality % 1 === 0;

        if (!isInt) throw 'argument is not an integer';

        if (newPlaybackQuality !== quality && newPlaybackQuality >= 0 && newPlaybackQuality <= getTopQualityIndexFor(type, id)) {
            setInternalQuality(type, id, newPlaybackQuality);
            eventBus.trigger(_coreEventsEventsJs2['default'].QUALITY_CHANGED, { mediaType: type, streamInfo: streamInfo, oldQuality: quality, newQuality: newPlaybackQuality });
        }
    }

    function setAbandonmentStateFor(type, state) {
        abandonmentStateDict[type].state = state;
    }

    function getAbandonmentStateFor(type) {
        return abandonmentStateDict[type].state;
    }

    /**
     * @param mediaInfo
     * @param bitrate A bitrate value, kbps
     * @returns {number} A quality index <= for the given bitrate
     * @memberof AbrController#
     */
    function getQualityForBitrate(mediaInfo, bitrate) {

        var bitrateList = getBitrateList(mediaInfo);
        var bitrateInfo = undefined;

        if (!bitrateList || bitrateList.length === 0) {
            return -1;
        }

        for (var i = bitrateList.length - 1; i >= 0; i--) {
            bitrateInfo = bitrateList[i];
            if (bitrate * 1000 >= bitrateInfo.bitrate) {
                return i;
            }
        }
        return 0;
    }

    /**
     * @param mediaInfo
     * @returns {Array} A list of {@link BitrateInfo} objects
     * @memberof AbrController#
     */
    function getBitrateList(mediaInfo) {
        if (!mediaInfo || !mediaInfo.bitrateList) return null;

        var bitrateList = mediaInfo.bitrateList;
        var type = mediaInfo.type;

        var infoList = [];
        var bitrateInfo;

        for (var i = 0, ln = bitrateList.length; i < ln; i++) {
            bitrateInfo = new _voBitrateInfoJs2['default']();
            bitrateInfo.mediaType = type;
            bitrateInfo.qualityIndex = i;
            bitrateInfo.bitrate = bitrateList[i].bandwidth;
            bitrateInfo.width = bitrateList[i].width;
            bitrateInfo.height = bitrateList[i].height;
            infoList.push(bitrateInfo);
        }

        return infoList;
    }

    function setAverageThroughput(type, value) {
        averageThroughputDict[type] = value;
    }

    function getAverageThroughput(type) {
        return averageThroughputDict[type];
    }

    function updateTopQualityIndex(mediaInfo) {
        var type = mediaInfo.type;
        var streamId = mediaInfo.streamInfo.id;
        var max = mediaInfo.representationCount - 1;

        setTopQualityIndex(type, streamId, max);

        return max;
    }

    function isPlayingAtTopQuality(streamInfo) {
        var isAtTop;
        var streamId = streamInfo.id;
        var audioQuality = getQualityFor('audio', streamInfo);
        var videoQuality = getQualityFor('video', streamInfo);

        isAtTop = audioQuality === getTopQualityIndexFor('audio', streamId) && videoQuality === getTopQualityIndexFor('video', streamId);

        return isAtTop;
    }

    function reset() {
        eventBus.off(_coreEventsEventsJs2['default'].LOADING_PROGRESS, onFragmentLoadProgress, this);
        clearTimeout(abandonmentTimeout);
        abandonmentTimeout = null;
        setup();
    }

    function getQualityFor(type, streamInfo) {
        var id = streamInfo.id;
        var quality;

        qualityDict[id] = qualityDict[id] || {};

        if (!qualityDict[id].hasOwnProperty(type)) {
            qualityDict[id][type] = 0;
        }

        quality = qualityDict[id][type];
        return quality;
    }

    function setInternalQuality(type, id, value) {
        qualityDict[id] = qualityDict[id] || {};
        qualityDict[id][type] = value;
    }

    function getConfidenceFor(type, id) {
        var confidence;

        confidenceDict[id] = confidenceDict[id] || {};

        if (!confidenceDict[id].hasOwnProperty(type)) {
            confidenceDict[id][type] = 0;
        }

        confidence = confidenceDict[id][type];

        return confidence;
    }

    function setConfidenceFor(type, id, value) {
        confidenceDict[id] = confidenceDict[id] || {};
        confidenceDict[id][type] = value;
    }

    function setTopQualityIndex(type, id, value) {
        topQualities[id] = topQualities[id] || {};
        topQualities[id][type] = value;
    }

    function checkMaxBitrate(idx, type) {
        var maxBitrate = getMaxAllowedBitrateFor(type);
        if (isNaN(maxBitrate) || !streamProcessorDict[type]) {
            return idx;
        }
        var maxIdx = getQualityForBitrate(streamProcessorDict[type].getMediaInfo(), maxBitrate);
        return Math.min(idx, maxIdx);
    }

    function checkMaxRepresentationRatio(idx, type, maxIdx) {
        var maxRepresentationRatio = getMaxAllowedRepresentationRatioFor(type);
        if (isNaN(maxRepresentationRatio) || maxRepresentationRatio >= 1 || maxRepresentationRatio < 0) {
            return idx;
        }
        return Math.min(idx, Math.round(maxIdx * maxRepresentationRatio));
    }

    function checkPortalSize(idx, type) {
        if (type !== 'video' || !limitBitrateByPortal || !streamProcessorDict[type]) {
            return idx;
        }

        var element = videoModel.getElement();
        var elementWidth = element.clientWidth;
        var elementHeight = element.clientHeight;
        var manifest = manifestModel.getValue();
        var representation = dashManifestModel.getAdaptationForType(manifest, 0, type).Representation;
        var newIdx = idx;

        if (elementWidth > 0 && elementHeight > 0) {
            while (newIdx > 0 && representation[newIdx] && elementWidth < representation[newIdx].width && elementWidth - representation[newIdx - 1].width < representation[newIdx].width - elementWidth) {
                newIdx = newIdx - 1;
            }

            if (representation.length - 2 >= newIdx && representation[newIdx].width === representation[newIdx + 1].width) {
                newIdx = Math.min(idx, newIdx + 1);
            }
        }

        return newIdx;
    }

    function onFragmentLoadProgress(e) {
        var type = e.request.mediaType;
        if (getAutoSwitchBitrateFor(type)) {
            //check to see if we are in manual or auto switch mode.

            var rules = abrRulesCollection.getRules(_rulesAbrABRRulesCollectionJs2['default'].ABANDON_FRAGMENT_RULES);
            var scheduleController = streamProcessorDict[type].getScheduleController();

            // There may be a fragment load in progress when we switch periods and recreated some controllers.
            // so return if that is the case.
            if (!scheduleController) return;

            var fragmentModel = scheduleController.getFragmentModel();
            var callback = function callback(switchRequest) {

                function setupTimeout(type) {
                    abandonmentTimeout = setTimeout(function () {
                        setAbandonmentStateFor(type, ALLOW_LOAD);
                    }, mediaPlayerModel.getAbandonLoadTimeout());
                }

                if (switchRequest.confidence === _rulesSwitchRequest2['default'].STRONG) {

                    var requests = fragmentModel.getRequests({ state: _modelsFragmentModelJs2['default'].FRAGMENT_MODEL_LOADING });
                    var newQuality = switchRequest.value;
                    var currentQuality = getQualityFor(type, streamController.getActiveStreamInfo());

                    if (newQuality < currentQuality) {

                        fragmentModel.abortRequests();
                        setAbandonmentStateFor(type, ABANDON_LOAD);
                        setPlaybackQuality(type, streamController.getActiveStreamInfo(), newQuality);
                        scheduleController.replaceCanceledRequests(requests);
                        setupTimeout(type);
                    }
                }
            };

            rulesController.applyRules(rules, streamProcessorDict[type], callback, e, function (currentValue, newValue) {
                return newValue;
            });
        }
    }

    instance = {
        isPlayingAtTopQuality: isPlayingAtTopQuality,
        updateTopQualityIndex: updateTopQualityIndex,
        getAverageThroughput: getAverageThroughput,
        getBitrateList: getBitrateList,
        getQualityForBitrate: getQualityForBitrate,
        getMaxAllowedBitrateFor: getMaxAllowedBitrateFor,
        setMaxAllowedBitrateFor: setMaxAllowedBitrateFor,
        getMaxAllowedRepresentationRatioFor: getMaxAllowedRepresentationRatioFor,
        setMaxAllowedRepresentationRatioFor: setMaxAllowedRepresentationRatioFor,
        getInitialBitrateFor: getInitialBitrateFor,
        setInitialBitrateFor: setInitialBitrateFor,
        getInitialRepresentationRatioFor: getInitialRepresentationRatioFor,
        setInitialRepresentationRatioFor: setInitialRepresentationRatioFor,
        setAutoSwitchBitrateFor: setAutoSwitchBitrateFor,
        getAutoSwitchBitrateFor: getAutoSwitchBitrateFor,
        setLimitBitrateByPortal: setLimitBitrateByPortal,
        getLimitBitrateByPortal: getLimitBitrateByPortal,
        getConfidenceFor: getConfidenceFor,
        getQualityFor: getQualityFor,
        getAbandonmentStateFor: getAbandonmentStateFor,
        setAbandonmentStateFor: setAbandonmentStateFor,
        setPlaybackQuality: setPlaybackQuality,
        getPlaybackQuality: getPlaybackQuality,
        setAverageThroughput: setAverageThroughput,
        getTopQualityIndexFor: getTopQualityIndexFor,
        initialize: initialize,
        setConfig: setConfig,
        reset: reset
    };

    setup();

    return instance;
}

AbrController.__dashjs_factory_name = 'AbrController';
var factory = _coreFactoryMakerJs2['default'].getSingletonFactory(AbrController);
factory.ABANDON_LOAD = ABANDON_LOAD;
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../../dash/models/DashManifestModel.js":17,"../models/FragmentModel.js":62,"../models/ManifestModel.js":63,"../models/MediaPlayerModel.js":64,"../models/VideoModel.js":67,"../rules/SwitchRequest":70,"../rules/abr/ABRRulesCollection.js":71,"../utils/DOMStorage.js":89,"../vo/BitrateInfo.js":98}],47:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _modelsBaseURLTreeModelJs = _dereq_('../models/BaseURLTreeModel.js');

var _modelsBaseURLTreeModelJs2 = _interopRequireDefault(_modelsBaseURLTreeModelJs);

var _utilsBaseURLSelectorJs = _dereq_('../utils/BaseURLSelector.js');

var _utilsBaseURLSelectorJs2 = _interopRequireDefault(_utilsBaseURLSelectorJs);

var _utilsURLUtilsJs = _dereq_('../utils/URLUtils.js');

var _utilsURLUtilsJs2 = _interopRequireDefault(_utilsURLUtilsJs);

var _dashVoBaseURLJs = _dereq_('../../dash/vo/BaseURL.js');

var _dashVoBaseURLJs2 = _interopRequireDefault(_dashVoBaseURLJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

function BaseURLController() {

    var instance = undefined;

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();
    var urlUtils = (0, _utilsURLUtilsJs2['default'])(context).getInstance();

    var baseURLTreeModel = undefined,
        baseURLSelector = undefined;

    function onBlackListChanged(e) {
        baseURLTreeModel.invalidateSelectedIndexes(e.entry);
    }

    function setup() {
        baseURLTreeModel = (0, _modelsBaseURLTreeModelJs2['default'])(context).create();
        baseURLSelector = (0, _utilsBaseURLSelectorJs2['default'])(context).create();

        eventBus.on(_coreEventsEventsJs2['default'].SERVICE_LOCATION_BLACKLIST_CHANGED, onBlackListChanged, instance);
    }

    function update(manifest) {
        baseURLTreeModel.update(manifest);
        baseURLSelector.chooseSelectorFromManifest(manifest);
    }

    function resolve(path) {
        var baseUrls = baseURLTreeModel.getForPath(path);

        var baseUrl = baseUrls.reduce(function (p, c) {
            var b = baseURLSelector.select(c);

            if (b) {
                if (!urlUtils.isRelative(b.url)) {
                    p.url = b.url;
                    p.serviceLocation = b.serviceLocation;
                } else {
                    p.url += b.url;
                }
            }

            return p;
        }, new _dashVoBaseURLJs2['default']());

        if (!urlUtils.isRelative(baseUrl.url)) {
            return baseUrl;
        }
    }

    function reset() {
        baseURLTreeModel.reset();
        baseURLSelector.reset();
    }

    function initialize(data) {
        update(data);
    }

    instance = {
        reset: reset,
        initialize: initialize,
        resolve: resolve
    };

    setup();

    return instance;
}

BaseURLController.__dashjs_factory_name = 'BaseURLController';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(BaseURLController);
module.exports = exports['default'];

},{"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../../dash/vo/BaseURL.js":26,"../models/BaseURLTreeModel.js":61,"../utils/BaseURLSelector.js":85,"../utils/URLUtils.js":96}],48:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

function BlackListController(config) {

    var blacklist = [];

    var eventBus = (0, _coreEventBusJs2['default'])(this.context).getInstance();
    var updateEventName = config.updateEventName;
    var loadFailedEventName = config.loadFailedEventName;

    function contains(query) {
        if (!blacklist.length || !query || !query.length) {
            return false;
        }

        return blacklist.indexOf(query) !== -1;
    }

    function add(entry) {
        if (blacklist.indexOf(entry) !== -1) {
            return;
        }

        blacklist.push(entry);

        eventBus.trigger(updateEventName, {
            entry: entry
        });
    }

    function onLoadFailed(e) {
        if (e.error) {
            add(e.request.serviceLocation);
        }
    }

    function setup() {
        if (loadFailedEventName) {
            eventBus.on(loadFailedEventName, onLoadFailed, this);
        }
    }

    function reset() {
        blacklist = [];
    }

    setup();

    return {
        add: add,
        contains: contains,
        reset: reset
    };
}

BlackListController.__dashjs_factory_name = 'BlackListController';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(BlackListController);
module.exports = exports['default'];

},{"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7}],49:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _modelsFragmentModelJs = _dereq_('../models/FragmentModel.js');

var _modelsFragmentModelJs2 = _interopRequireDefault(_modelsFragmentModelJs);

var _modelsMediaPlayerModelJs = _dereq_('../models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

var _voMetricsHTTPRequestJs = _dereq_('../vo/metrics/HTTPRequest.js');

var _voMetricsHTTPRequestJs2 = _interopRequireDefault(_voMetricsHTTPRequestJs);

var _SourceBufferControllerJs = _dereq_('./SourceBufferController.js');

var _SourceBufferControllerJs2 = _interopRequireDefault(_SourceBufferControllerJs);

var _AbrControllerJs = _dereq_('./AbrController.js');

var _AbrControllerJs2 = _interopRequireDefault(_AbrControllerJs);

var _PlaybackControllerJs = _dereq_('./PlaybackController.js');

var _PlaybackControllerJs2 = _interopRequireDefault(_PlaybackControllerJs);

var _MediaControllerJs = _dereq_('./MediaController.js');

var _MediaControllerJs2 = _interopRequireDefault(_MediaControllerJs);

var _utilsCustomTimeRangesJs = _dereq_('../utils/CustomTimeRanges.js');

var _utilsCustomTimeRangesJs2 = _interopRequireDefault(_utilsCustomTimeRangesJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _utilsBoxParserJs = _dereq_('../utils/BoxParser.js');

var _utilsBoxParserJs2 = _interopRequireDefault(_utilsBoxParserJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var BUFFER_LOADED = 'bufferLoaded';
var BUFFER_EMPTY = 'bufferStalled';
var STALL_THRESHOLD = 0.5;

function BufferController(config) {

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var metricsModel = config.metricsModel;
    var manifestModel = config.manifestModel;
    var sourceBufferController = config.sourceBufferController;
    var errHandler = config.errHandler;
    var mediaSourceController = config.mediaSourceController;
    var streamController = config.streamController;
    var mediaController = config.mediaController;
    var adapter = config.adapter;
    var virtualBuffer = config.virtualBuffer;
    var textSourceBuffer = config.textSourceBuffer;

    var instance = undefined,
        requiredQuality = undefined,
        currentQuality = undefined,
        isBufferingCompleted = undefined,
        bufferLevel = undefined,
        bufferTarget = undefined,
        criticalBufferLevel = undefined,
        mediaSource = undefined,
        maxAppendedIndex = undefined,
        lastIndex = undefined,
        type = undefined,
        buffer = undefined,
        bufferState = undefined,
        appendedBytesInfo = undefined,
        wallclockTicked = undefined,
        appendingMediaChunk = undefined,
        isAppendingInProgress = undefined,
        isPruningInProgress = undefined,
        inbandEventFound = undefined,
        playbackController = undefined,
        streamProcessor = undefined,
        abrController = undefined,
        fragmentController = undefined,
        scheduleController = undefined,
        mediaPlayerModel = undefined,
        clearBufferTimeout = undefined,
        extraData = undefined;

    function setup() {
        requiredQuality = -1;
        currentQuality = -1;
        isBufferingCompleted = false;
        bufferLevel = 0;
        bufferTarget = 0;
        criticalBufferLevel = Number.POSITIVE_INFINITY;
        maxAppendedIndex = -1;
        lastIndex = -1;
        buffer = null;
        bufferState = BUFFER_EMPTY;
        wallclockTicked = 0;
        appendingMediaChunk = false;
        isAppendingInProgress = false;
        isPruningInProgress = false;
        inbandEventFound = false;
        clearBufferTimeout = null;
    }

    function initialize(Type, Source, StreamProcessor) {
        type = Type;
        setMediaSource(Source);
        streamProcessor = StreamProcessor;
        mediaPlayerModel = (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance();
        playbackController = (0, _PlaybackControllerJs2['default'])(context).getInstance();
        abrController = (0, _AbrControllerJs2['default'])(context).getInstance();
        fragmentController = streamProcessor.getFragmentController();
        scheduleController = streamProcessor.getScheduleController();
        requiredQuality = abrController.getQualityFor(type, streamProcessor.getStreamInfo());

        eventBus.on(_coreEventsEventsJs2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this);
        eventBus.on(_coreEventsEventsJs2['default'].INIT_FRAGMENT_LOADED, onInitFragmentLoaded, this);
        eventBus.on(_coreEventsEventsJs2['default'].MEDIA_FRAGMENT_LOADED, onMediaFragmentLoaded, this);
        eventBus.on(_coreEventsEventsJs2['default'].QUALITY_CHANGED, onQualityChanged, this);
        eventBus.on(_coreEventsEventsJs2['default'].STREAM_COMPLETED, onStreamCompleted, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_PROGRESS, onPlaybackProgression, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_TIME_UPDATED, onPlaybackProgression, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_RATE_CHANGED, onPlaybackRateChanged, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, this);
        eventBus.on(_coreEventsEventsJs2['default'].WALLCLOCK_TIME_UPDATED, onWallclockTimeUpdated, this);
        eventBus.on(_coreEventsEventsJs2['default'].CURRENT_TRACK_CHANGED, onCurrentTrackChanged, this);
        eventBus.on(_coreEventsEventsJs2['default'].SOURCEBUFFER_APPEND_COMPLETED, onAppended, this);
        eventBus.on(_coreEventsEventsJs2['default'].SOURCEBUFFER_REMOVE_COMPLETED, onRemoved, this);
        eventBus.on(_coreEventsEventsJs2['default'].CHUNK_APPENDED, onChunkAppended, this);
    }

    function createBuffer(mediaInfo) {
        if (!mediaInfo || !mediaSource || !streamProcessor) return null;

        var sourceBuffer = null;

        try {
            sourceBuffer = sourceBufferController.createSourceBuffer(mediaSource, mediaInfo);

            if (sourceBuffer && sourceBuffer.hasOwnProperty('initialize')) {
                sourceBuffer.initialize(type, this);
            }
        } catch (e) {
            errHandler.mediaSourceError('Error creating ' + type + ' source buffer.');
        }

        setBuffer(sourceBuffer);
        updateBufferTimestampOffset(streamProcessor.getRepresentationInfoForQuality(requiredQuality).MSETimeOffset);
        // We may already have some segments in a virtual buffer by this moment. Let's try to append them to the real one.
        appendNext();

        return sourceBuffer;
    }

    function isActive() {
        var thisStreamId = streamProcessor.getStreamInfo().id;
        var activeStreamId = streamController.getActiveStreamInfo().id;

        return thisStreamId === activeStreamId;
    }

    function onInitFragmentLoaded(e) {
        // We received a new init chunk.
        // We just want to cache it in the virtual buffer here.
        // Then pass control to appendNext() to handle any other logic.

        var chunk;

        if (e.fragmentModel !== streamProcessor.getFragmentModel()) return;

        log('Initialization finished loading');
        chunk = e.chunk;
        // cache the initialization data to use it next time the quality has changed
        virtualBuffer.append(chunk);
        switchInitData(getStreamId(), requiredQuality);

        if (chunk.mediaInfo.type === 'video') {
            extraData = (0, _utilsBoxParserJs2['default'])(context).getInstance().avccExtraData(chunk.bytes);
        }
    }

    function onMediaFragmentLoaded(e) {
        if (e.fragmentModel !== streamProcessor.getFragmentModel()) return;

        var events;
        var chunk = e.chunk;
        var bytes = chunk.bytes;
        var quality = chunk.quality;
        var index = chunk.index;
        var request = streamProcessor.getFragmentModel().getRequests({ state: _modelsFragmentModelJs2['default'].FRAGMENT_MODEL_EXECUTED, quality: quality, index: index })[0];
        var currentRepresentation = streamProcessor.getRepresentationInfoForQuality(quality);
        var manifest = manifestModel.getValue();
        var eventStreamMedia = adapter.getEventsFor(manifest, currentRepresentation.mediaInfo, streamProcessor);
        var eventStreamTrack = adapter.getEventsFor(manifest, currentRepresentation, streamProcessor);

        if (eventStreamMedia.length > 0 || eventStreamTrack.length > 0) {
            events = handleInbandEvents(bytes, request, eventStreamMedia, eventStreamTrack);
            streamProcessor.getEventController().addInbandEvents(events);
        }

        if (mediaController.getInitialSettings('checkEncoding') && request.mediaType === 'video') {
            var samples = (0, _utilsBoxParserJs2['default'])(context).getInstance().getSyncSamples(extraData, bytes);
            eventBus.trigger(_coreEventsEventsJs2['default'].SEGMENT_SYNC_SAMPLES, { index: index, quality: quality, samples: samples });
        }

        chunk.bytes = deleteInbandEvents(bytes);

        virtualBuffer.append(chunk);
        appendNext();
    }

    function appendNext() {
        // If we have an appendingMediaChunk in progress, process it.
        // Otherwise, try to get a media chunk from the virtual buffer.
        // If we have no media chunk available, do nothing - return.
        // If the media chunk we have matches currentQuality, append the media chunk to the source buffer.
        // Otherwise, leave the media chunk in appendingMediaChunk and check the init chunk corresponding to the media chunk.
        // If we have the corresponding init chunk, append the init chunk to the source buffer; appendingMediaChunk will be processed shortly through onAppended().
        // Otherwise, fire the Events.INIT_REQUESTED event.
        if (!buffer || isAppendingInProgress || !hasEnoughSpaceToAppend()) return;

        var streamId = getStreamId();
        var chunk;

        if (appendingMediaChunk) {
            chunk = appendingMediaChunk;
        } else {

            chunk = virtualBuffer.extract({ streamId: streamId, mediaType: type, segmentType: _voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE, limit: 1 })[0];
            if (!chunk) {
                return;
            }

            appendingMediaChunk = chunk;
        }

        if (chunk.quality === currentQuality) {
            appendingMediaChunk = false;
            appendToBuffer(chunk);
        } else {
            // we need to change currentQuality by init data
            switchInitData(streamId, appendingMediaChunk.quality);
        }
    }

    function switchInitData(streamId, quality) {

        var filter = { streamId: streamId, mediaType: type, segmentType: _voMetricsHTTPRequestJs2['default'].INIT_SEGMENT_TYPE, quality: quality };
        var chunk = virtualBuffer.getChunks(filter)[0];

        if (chunk) {
            if (!buffer) return;

            appendToBuffer(chunk);
        } else {
            // if we have not loaded the init fragment for the current quality, do it
            eventBus.trigger(_coreEventsEventsJs2['default'].INIT_REQUESTED, { sender: instance, requiredQuality: quality });
        }
    }

    function appendToBuffer(chunk) {
        isAppendingInProgress = true;
        appendedBytesInfo = chunk;
        sourceBufferController.append(buffer, chunk);

        if (chunk.mediaInfo.type === 'video') {
            if (chunk.mediaInfo.embeddedCaptions) {
                textSourceBuffer.append(chunk.bytes, chunk);
            }
        }
    }

    function onAppended(e) {
        if (buffer !== e.buffer) return;

        onPlaybackProgression();

        if (isBufferingCompleted && streamProcessor.getStreamInfo().isLast) {
            mediaSourceController.signalEndOfStream(mediaSource);
        }

        var ranges;

        if (e.error) {
            // if the append has failed because the buffer is full we should store the data
            // that has not been appended and stop request scheduling. We also need to store
            // the promise for this append because the next data can be appended only after
            // this promise is resolved.
            if (e.error.code === _SourceBufferControllerJs2['default'].QUOTA_EXCEEDED_ERROR_CODE) {
                virtualBuffer.append(appendedBytesInfo);
                criticalBufferLevel = sourceBufferController.getTotalBufferedTime(buffer) * 0.8;
                eventBus.trigger(_coreEventsEventsJs2['default'].QUOTA_EXCEEDED, { sender: instance, criticalBufferLevel: criticalBufferLevel });
                clearBuffer(getClearRange());
            }
            isAppendingInProgress = false;
            return;
        }

        if (!hasEnoughSpaceToAppend()) {
            eventBus.trigger(_coreEventsEventsJs2['default'].QUOTA_EXCEEDED, { sender: instance, criticalBufferLevel: criticalBufferLevel });
            clearBuffer(getClearRange());
        }

        ranges = sourceBufferController.getAllRanges(buffer);

        if (ranges) {
            //log("Append complete: " + ranges.length);
            if (ranges.length > 0) {
                var i, len;

                //log("Number of buffered ranges: " + ranges.length);
                for (i = 0, len = ranges.length; i < len; i++) {
                    log('Buffered Range: ' + ranges.start(i) + ' - ' + ranges.end(i));
                }
            }
        }

        //finish appending
        isAppendingInProgress = false;
        if (!isNaN(appendedBytesInfo.index)) {
            virtualBuffer.storeAppendedChunk(appendedBytesInfo, buffer);
            removeOldTrackData();
            maxAppendedIndex = Math.max(appendedBytesInfo.index, maxAppendedIndex);
            checkIfBufferingCompleted();
        } else {
            currentQuality = appendedBytesInfo.quality;
            if (!streamProcessor.isDynamic()) {
                appendNext();
            }
        }

        eventBus.trigger(_coreEventsEventsJs2['default'].BYTES_APPENDED, { sender: instance, quality: appendedBytesInfo.quality, startTime: appendedBytesInfo.start, index: appendedBytesInfo.index, bufferedRanges: ranges });
    }

    function onQualityChanged(e) {
        var newQuality = e.newQuality;
        if (requiredQuality === newQuality || type !== e.mediaType || streamProcessor.getStreamInfo().id !== e.streamInfo.id) return;

        updateBufferTimestampOffset(streamProcessor.getRepresentationInfoForQuality(newQuality).MSETimeOffset);
        requiredQuality = newQuality;
    }

    //**********************************************************************
    // START Buffer Level, State & Sufficiency Handling.
    //**********************************************************************
    function onPlaybackSeeking() {
        isAppendingInProgress = false;
        onPlaybackProgression();
    }

    function onPlaybackProgression() {
        updateBufferLevel();
        addBufferMetrics();
    }

    function updateBufferLevel() {
        var currentTime = playbackController.getTime();

        bufferLevel = sourceBufferController.getBufferLength(buffer, currentTime);
        eventBus.trigger(_coreEventsEventsJs2['default'].BUFFER_LEVEL_UPDATED, { sender: instance, bufferLevel: bufferLevel });
        checkIfSufficientBuffer();
    }

    function addBufferMetrics() {
        if (!isActive()) return;

        //TODO will need to fix how we get bufferTarget... since we ony load one at a time. but do it in the addBufferMetrics call not here
        //bufferTarget = fragmentsToLoad > 0 ? (fragmentsToLoad * fragmentDuration) + bufferLevel : bufferTarget;
        metricsModel.addBufferState(type, bufferState, bufferTarget);

        //TODO may be needed for MULTIPERIOD PLEASE CHECK Turning this off for now... not really needed since we load sync...
        //var level = bufferLevel,
        //    virtualLevel;
        //virtualLevel = virtualBuffer.getTotalBufferLevel(streamProcessor.getMediaInfo());
        //if (virtualLevel) {
        //    level += virtualLevel;
        //}

        metricsModel.addBufferLevel(type, new Date(), bufferLevel * 1000);
    }

    function checkIfBufferingCompleted() {
        var isLastIdxAppended = maxAppendedIndex === lastIndex - 1;

        if (!isLastIdxAppended || isBufferingCompleted) return;

        isBufferingCompleted = true;
        eventBus.trigger(_coreEventsEventsJs2['default'].BUFFERING_COMPLETED, { sender: instance, streamInfo: streamProcessor.getStreamInfo() });
    }

    function checkIfSufficientBuffer() {
        if (bufferLevel < STALL_THRESHOLD && !isBufferingCompleted) {
            notifyBufferStateChanged(BUFFER_EMPTY);
        } else {
            notifyBufferStateChanged(BUFFER_LOADED);
        }
    }

    function notifyBufferStateChanged(state) {
        if (bufferState === state || type === 'fragmentedText' && textSourceBuffer.getAllTracksAreDisabled()) return;

        bufferState = state;
        addBufferMetrics();
        eventBus.trigger(_coreEventsEventsJs2['default'].BUFFER_LEVEL_STATE_CHANGED, { sender: instance, state: state, mediaType: type, streamInfo: streamProcessor.getStreamInfo() });
        var eventType = state === BUFFER_LOADED ? _coreEventsEventsJs2['default'].BUFFER_LOADED : _coreEventsEventsJs2['default'].BUFFER_EMPTY;
        eventBus.trigger(eventType, { mediaType: type });
        log(state === BUFFER_LOADED ? 'Got enough buffer to start.' : 'Waiting for more buffer before starting playback.');
    }

    function handleInbandEvents(data, request, mediaInbandEvents, trackInbandEvents) {
        var fragmentStarttime = Math.max(isNaN(request.startTime) ? 0 : request.startTime, 0);
        var eventStreams = [];
        var events = [];

        var eventBoxes, event, isoFile, inbandEvents;

        inbandEventFound = false;
        /* Extract the possible schemeIdUri : If a DASH client detects an event message box with a scheme that is not defined in MPD, the client is expected to ignore it */
        inbandEvents = mediaInbandEvents.concat(trackInbandEvents);
        for (var loop = 0; loop < inbandEvents.length; loop++) {
            eventStreams[inbandEvents[loop].schemeIdUri] = inbandEvents[loop];
        }

        isoFile = (0, _utilsBoxParserJs2['default'])(context).getInstance().parse(data);
        eventBoxes = isoFile.getBoxes('emsg');

        for (var i = 0, ln = eventBoxes.length; i < ln; i++) {
            event = adapter.getEvent(eventBoxes[i], eventStreams, fragmentStarttime);

            if (event) {
                events.push(event);
            }
        }

        return events;
    }

    function deleteInbandEvents(data) {

        if (!inbandEventFound) {
            return data;
        }

        var length = data.length;
        var expTwo = Math.pow(256, 2);
        var expThree = Math.pow(256, 3);
        var modData = new Uint8Array(data.length);

        var identifier, size;
        var i = 0;
        var j = 0;

        while (i < length) {

            identifier = String.fromCharCode(data[i + 4], data[i + 5], data[i + 6], data[i + 7]);
            size = data[i] * expThree + data[i + 1] * expTwo + data[i + 2] * 256 + data[i + 3] * 1;

            if (identifier != 'emsg') {
                for (var l = i; l < i + size; l++) {
                    modData[j] = data[l];
                    j++;
                }
            }
            i += size;
        }

        return modData.subarray(0, j);
    }

    function hasEnoughSpaceToAppend() {
        var totalBufferedTime = sourceBufferController.getTotalBufferedTime(buffer);
        return totalBufferedTime < criticalBufferLevel;
    }

    /* prune buffer on our own in background to avoid browsers pruning buffer silently */
    function pruneBuffer() {
        if (type === 'fragmentedText') return;

        log('try to prune buffer');

        var start = buffer.buffered.length ? buffer.buffered.start(0) : 0;
        var currentTime = playbackController.getTime();
        // we want to get rid off buffer that is more than x seconds behind current time
        var bufferToPrune = currentTime - start - mediaPlayerModel.getBufferToKeep();

        if (bufferToPrune > 0) {
            log('pruning buffer: ' + bufferToPrune + ' seconds.');
            isPruningInProgress = true;
            sourceBufferController.remove(buffer, 0, Math.round(start + bufferToPrune), mediaSource);
        }
    }

    function getClearRange() {
        var currentTime, removeStart, removeEnd, range, req;

        if (!buffer) return null;

        currentTime = playbackController.getTime();
        // we need to remove data that is more than one fragment before the video currentTime
        req = streamProcessor.getFragmentModel().getRequests({ state: _modelsFragmentModelJs2['default'].FRAGMENT_MODEL_EXECUTED, time: currentTime })[0];
        removeEnd = req && !isNaN(req.startTime) ? req.startTime : Math.floor(currentTime);

        range = sourceBufferController.getBufferRange(buffer, currentTime);

        if (range === null && buffer.buffered.length > 0) {
            removeEnd = buffer.buffered.end(buffer.buffered.length - 1);
        }

        removeStart = buffer.buffered.start(0);

        return { start: removeStart, end: removeEnd };
    }

    function clearBuffer(range) {
        if (!range || !buffer) return;

        var removeStart = range.start;
        var removeEnd = range.end;

        sourceBufferController.remove(buffer, removeStart, removeEnd, mediaSource);
    }

    function onRemoved(e) {
        if (buffer !== e.buffer) return;

        // After the buffer has been cleared we need to update the virtual range that reflects the actual ranges
        // of SourceBuffer. We also need to update the list of appended chunks
        if (isPruningInProgress) {
            isPruningInProgress = false;
        }
        virtualBuffer.updateBufferedRanges({ streamId: getStreamId(), mediaType: type }, sourceBufferController.getAllRanges(buffer));
        updateBufferLevel();
        eventBus.trigger(_coreEventsEventsJs2['default'].BUFFER_CLEARED, { sender: instance, from: e.from, to: e.to, hasEnoughSpaceToAppend: hasEnoughSpaceToAppend() });
        if (hasEnoughSpaceToAppend()) return;

        if (clearBufferTimeout === null) {
            clearBufferTimeout = setTimeout(function () {
                clearBufferTimeout = null;
                clearBuffer(getClearRange());
            }, streamProcessor.getStreamInfo().manifestInfo.minBufferTime * 1000);
        }
    }

    function updateBufferTimestampOffset(MSETimeOffset) {
        // each track can have its own @presentationTimeOffset, so we should set the offset
        // if it has changed after switching the quality or updating an mpd
        if (buffer && buffer.timestampOffset !== MSETimeOffset && !isNaN(MSETimeOffset)) {
            buffer.timestampOffset = MSETimeOffset;
        }
    }

    function getStreamId() {
        return streamProcessor.getStreamInfo().id;
    }

    function removeOldTrackData() {
        var allAppendedChunks = virtualBuffer.getChunks({ streamId: getStreamId(), mediaType: type, segmentType: _voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE, appended: true });

        var customTimeRangesFactory = (0, _utilsCustomTimeRangesJs2['default'])(context);
        var rangesToClear = customTimeRangesFactory.create();
        var rangesToLeave = customTimeRangesFactory.create();

        var currentTime = playbackController.getTime();
        var safeBufferLength = streamProcessor.getCurrentRepresentationInfo().fragmentDuration * 2;

        var currentTrackBufferLength, ranges, range;

        allAppendedChunks.forEach(function (chunk) {
            ranges = mediaController.isCurrentTrack(chunk.mediaInfo) ? rangesToLeave : rangesToClear;
            ranges.add(chunk.bufferedRange.start, chunk.bufferedRange.end);
        });

        if (rangesToClear.length === 0 || rangesToLeave.length === 0) return;

        currentTrackBufferLength = sourceBufferController.getBufferLength({ buffered: rangesToLeave }, currentTime);

        if (currentTrackBufferLength < safeBufferLength) return;

        for (var i = 0, ln = rangesToClear.length; i < ln; i++) {
            range = { start: rangesToClear.start(i), end: rangesToClear.end(i) };
            if (mediaController.getSwitchMode(type) === _MediaControllerJs2['default'].TRACK_SWITCH_MODE_ALWAYS_REPLACE || range.start > currentTime) {
                clearBuffer(range);
            }
        }
    }

    function onDataUpdateCompleted(e) {
        if (e.sender.getStreamProcessor() !== streamProcessor) return;
        if (e.error) return;

        updateBufferTimestampOffset(e.currentRepresentation.MSETimeOffset);
    }

    function onStreamCompleted(e) {
        if (e.fragmentModel !== streamProcessor.getFragmentModel()) return;
        lastIndex = e.request.index;
        checkIfBufferingCompleted();
    }

    function onChunkAppended(e) {
        if (e.sender !== virtualBuffer) return;
        addBufferMetrics();
    }

    function onCurrentTrackChanged(e) {
        if (!buffer || e.newMediaInfo.type !== type || e.newMediaInfo.streamInfo.id !== streamProcessor.getStreamInfo().id) return;

        var newMediaInfo = e.newMediaInfo;
        var mediaType = newMediaInfo.type;
        var switchMode = e.switchMode;
        var currentTime = playbackController.getTime();
        var range = { start: 0, end: currentTime };

        if (type !== mediaType) return;

        switch (switchMode) {
            case _MediaControllerJs2['default'].TRACK_SWITCH_MODE_ALWAYS_REPLACE:
                clearBuffer(range);
                break;
            case _MediaControllerJs2['default'].TRACK_SWITCH_MODE_NEVER_REPLACE:
                break;
            default:
                log('track switch mode is not supported: ' + switchMode);
        }
    }

    function onWallclockTimeUpdated() {
        var secondsElapsed;
        //constantly prune buffer every x seconds
        wallclockTicked++;
        secondsElapsed = wallclockTicked * (mediaPlayerModel.getWallclockTimeUpdateInterval() / 1000);
        if (secondsElapsed >= mediaPlayerModel.getBufferPruningInterval() && !isAppendingInProgress) {
            wallclockTicked = 0;
            pruneBuffer();
        }
    }

    function onPlaybackRateChanged() {
        checkIfSufficientBuffer();
    }

    function getType() {
        return type;
    }

    function getStreamProcessor() {
        return streamProcessor;
    }

    function setStreamProcessor(value) {
        streamProcessor = value;
    }

    function getBuffer() {
        return buffer;
    }

    function setBuffer(value) {
        buffer = value;
    }

    function getBufferLevel() {
        return bufferLevel;
    }

    function getCriticalBufferLevel() {
        return criticalBufferLevel;
    }

    function setMediaSource(value) {
        mediaSource = value;
    }

    function getMediaSource() {
        return mediaSource;
    }

    function getIsBufferingCompleted() {
        return isBufferingCompleted;
    }

    function getIsAppendingInProgress() {
        return isAppendingInProgress;
    }

    function reset(errored) {

        eventBus.off(_coreEventsEventsJs2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this);
        eventBus.off(_coreEventsEventsJs2['default'].QUALITY_CHANGED, onQualityChanged, this);
        eventBus.off(_coreEventsEventsJs2['default'].INIT_FRAGMENT_LOADED, onInitFragmentLoaded, this);
        eventBus.off(_coreEventsEventsJs2['default'].MEDIA_FRAGMENT_LOADED, onMediaFragmentLoaded, this);
        eventBus.off(_coreEventsEventsJs2['default'].STREAM_COMPLETED, onStreamCompleted, this);
        eventBus.off(_coreEventsEventsJs2['default'].CURRENT_TRACK_CHANGED, onCurrentTrackChanged, this);
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_PROGRESS, onPlaybackProgression, this);
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_TIME_UPDATED, onPlaybackProgression, this);
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_RATE_CHANGED, onPlaybackRateChanged, this);
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, this);
        eventBus.off(_coreEventsEventsJs2['default'].WALLCLOCK_TIME_UPDATED, onWallclockTimeUpdated, this);
        eventBus.off(_coreEventsEventsJs2['default'].SOURCEBUFFER_APPEND_COMPLETED, onAppended, this);
        eventBus.off(_coreEventsEventsJs2['default'].SOURCEBUFFER_REMOVE_COMPLETED, onRemoved, this);
        eventBus.off(_coreEventsEventsJs2['default'].CHUNK_APPENDED, onChunkAppended, this);

        clearTimeout(clearBufferTimeout);
        clearBufferTimeout = null;

        criticalBufferLevel = Number.POSITIVE_INFINITY;
        bufferState = BUFFER_EMPTY;
        currentQuality = -1;
        lastIndex = -1;
        maxAppendedIndex = -1;
        requiredQuality = 0;
        appendedBytesInfo = null;
        appendingMediaChunk = false;
        isBufferingCompleted = false;
        isAppendingInProgress = false;
        isPruningInProgress = false;
        playbackController = null;
        streamProcessor = null;
        abrController = null;
        fragmentController = null;
        scheduleController = null;

        if (!errored) {
            sourceBufferController.abort(mediaSource, buffer);
            sourceBufferController.removeSourceBuffer(mediaSource, buffer);
        }

        buffer = null;
    }

    instance = {
        initialize: initialize,
        createBuffer: createBuffer,
        getType: getType,
        getStreamProcessor: getStreamProcessor,
        setStreamProcessor: setStreamProcessor,
        getBuffer: getBuffer,
        setBuffer: setBuffer,
        getBufferLevel: getBufferLevel,
        getCriticalBufferLevel: getCriticalBufferLevel,
        setMediaSource: setMediaSource,
        getMediaSource: getMediaSource,
        getIsBufferingCompleted: getIsBufferingCompleted,
        getIsAppendingInProgress: getIsAppendingInProgress,
        reset: reset
    };

    setup();

    return instance;
}

BufferController.__dashjs_factory_name = 'BufferController';
var factory = _coreFactoryMakerJs2['default'].getClassFactory(BufferController);
factory.BUFFER_LOADED = BUFFER_LOADED;
factory.BUFFER_EMPTY = BUFFER_EMPTY;
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/Debug.js":5,"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../models/FragmentModel.js":62,"../models/MediaPlayerModel.js":64,"../utils/BoxParser.js":86,"../utils/CustomTimeRanges.js":88,"../vo/metrics/HTTPRequest.js":117,"./AbrController.js":46,"./MediaController.js":52,"./PlaybackController.js":54,"./SourceBufferController.js":56}],50:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _controllersPlaybackControllerJs = _dereq_('../controllers/PlaybackController.js');

var _controllersPlaybackControllerJs2 = _interopRequireDefault(_controllersPlaybackControllerJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

function EventController() {

    var MPD_RELOAD_SCHEME = 'urn:mpeg:dash:event:2012';
    var MPD_RELOAD_VALUE = 1;

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        inlineEvents = undefined,
        // Holds all Inline Events not triggered yet
    inbandEvents = undefined,
        // Holds all Inband Events not triggered yet
    activeEvents = undefined,
        // Holds all Events currently running
    eventInterval = undefined,
        // variable holding the setInterval
    refreshDelay = undefined,
        // refreshTime for the setInterval
    presentationTimeThreshold = undefined,
        manifestModel = undefined,
        manifestUpdater = undefined,
        playbackController = undefined,
        isStarted = undefined;

    function initialize() {
        isStarted = false;
        inlineEvents = {};
        inbandEvents = {};
        activeEvents = {};
        eventInterval = null;
        refreshDelay = 100;
        presentationTimeThreshold = refreshDelay / 1000;
        playbackController = (0, _controllersPlaybackControllerJs2['default'])(context).getInstance();
    }

    function clear() {
        if (eventInterval !== null && isStarted) {
            clearInterval(eventInterval);
            eventInterval = null;
            isStarted = false;
        }
    }

    function start() {
        log('Start Event Controller');
        if (!isStarted && !isNaN(refreshDelay)) {
            isStarted = true;
            eventInterval = setInterval(onEventTimer, refreshDelay);
        }
    }

    /**
     * Add events to the eventList. Events that are not in the mpd anymore but not triggered yet will still be deleted
     * @param values
     */
    function addInlineEvents(values) {
        inlineEvents = {};

        if (values) {
            for (var i = 0; i < values.length; i++) {
                var event = values[i];
                inlineEvents[event.id] = event;
                log('Add inline event with id ' + event.id);
            }
        }
        log('Added ' + values.length + ' inline events');
    }

    /**
     * i.e. processing of any one event message box with the same id is sufficient
     * @param values
     */
    function addInbandEvents(values) {
        for (var i = 0; i < values.length; i++) {
            var event = values[i];
            if (!(event.id in inbandEvents)) {
                inbandEvents[event.id] = event;
                log('Add inband event with id ' + event.id);
            } else {
                log('Repeated event with id ' + event.id);
            }
        }
    }

    /**
     * Remove events which are over from the list
     */
    function removeEvents() {
        if (activeEvents) {
            var currentVideoTime = playbackController.getTime();
            var eventIds = Object.keys(activeEvents);

            for (var i = 0; i < eventIds.length; i++) {
                var eventId = eventIds[i];
                var curr = activeEvents[eventId];
                if (curr !== null && (curr.duration + curr.presentationTime) / curr.eventStream.timescale < currentVideoTime) {
                    log('Remove Event ' + eventId + ' at time ' + currentVideoTime);
                    curr = null;
                    delete activeEvents[eventId];
                }
            }
        }
    }

    /**
     * Iterate through the eventList and trigger/remove the events
     */
    function onEventTimer() {
        triggerEvents(inbandEvents);
        triggerEvents(inlineEvents);
        removeEvents();
    }

    function refreshManifest() {
        var manifest = manifestModel.getValue();
        var url = manifest.url;

        if (manifest.hasOwnProperty('Location')) {
            url = manifest.Location;
        }
        log('Refresh manifest @ ' + url);
        manifestUpdater.getManifestLoader().load(url);
    }

    function triggerEvents(events) {
        var currentVideoTime = playbackController.getTime();
        var presentationTime;

        /* == Trigger events that are ready == */
        if (events) {
            var eventIds = Object.keys(events);
            for (var i = 0; i < eventIds.length; i++) {
                var eventId = eventIds[i];
                var curr = events[eventId];

                if (curr !== undefined) {
                    presentationTime = curr.presentationTime / curr.eventStream.timescale;
                    if (presentationTime === 0 || presentationTime <= currentVideoTime && presentationTime + presentationTimeThreshold > currentVideoTime) {
                        log('Start Event ' + eventId + ' at ' + currentVideoTime);
                        if (curr.duration > 0) {
                            activeEvents[eventId] = curr;
                        }
                        if (curr.eventStream.schemeIdUri == MPD_RELOAD_SCHEME && curr.eventStream.value == MPD_RELOAD_VALUE) {
                            refreshManifest();
                        } else {
                            eventBus.trigger(curr.eventStream.schemeIdUri, { event: curr });
                        }
                        delete events[eventId];
                    }
                }
            }
        }
    }

    function setConfig(config) {
        if (!config) return;

        if (config.manifestModel) {
            manifestModel = config.manifestModel;
        }

        if (config.manifestUpdater) {
            manifestUpdater = config.manifestUpdater;
        }
    }

    function reset() {
        clear();
        inlineEvents = null;
        inbandEvents = null;
        activeEvents = null;
        playbackController = null;
    }

    instance = {
        initialize: initialize,
        addInlineEvents: addInlineEvents,
        addInbandEvents: addInbandEvents,
        clear: clear,
        start: start,
        setConfig: setConfig,
        reset: reset
    };

    return instance;
}

EventController.__dashjs_factory_name = 'EventController';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(EventController);
module.exports = exports['default'];

},{"../../core/Debug.js":5,"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../controllers/PlaybackController.js":54}],51:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _voMetricsHTTPRequestJs = _dereq_('../vo/metrics/HTTPRequest.js');

var _voMetricsHTTPRequestJs2 = _interopRequireDefault(_voMetricsHTTPRequestJs);

var _voDataChunkJs = _dereq_('../vo/DataChunk.js');

var _voDataChunkJs2 = _interopRequireDefault(_voDataChunkJs);

var _modelsFragmentModelJs = _dereq_('../models/FragmentModel.js');

var _modelsFragmentModelJs2 = _interopRequireDefault(_modelsFragmentModelJs);

var _modelsMetricsModelJs = _dereq_('../models/MetricsModel.js');

var _modelsMetricsModelJs2 = _interopRequireDefault(_modelsMetricsModelJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

function FragmentController() /*config*/{

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        fragmentModels = undefined;

    function setup() {
        fragmentModels = [];
        eventBus.on(_coreEventsEventsJs2['default'].FRAGMENT_LOADING_COMPLETED, onFragmentLoadingCompleted, instance);
    }

    function process(bytes) {
        var result = null;

        if (bytes !== null && bytes !== undefined && bytes.byteLength > 0) {
            result = new Uint8Array(bytes);
        }

        return result;
    }

    function getModel(scheduleController) {
        if (!scheduleController) return null;
        // Wrap the buffer controller into model and store it to track the loading state and execute the requests
        var model = findModel(scheduleController);

        if (!model) {
            model = (0, _modelsFragmentModelJs2['default'])(context).create({ metricsModel: (0, _modelsMetricsModelJs2['default'])(context).getInstance() });
            model.setScheduleController(scheduleController);
            fragmentModels.push(model);
        }

        return model;
    }

    function detachModel(model) {
        var idx = fragmentModels.indexOf(model);
        // If we have the model for the given buffer just remove it from array
        if (idx > -1) {
            fragmentModels.splice(idx, 1);
        }
    }

    function isInitializationRequest(request) {
        return request && request.type && request.type === _voMetricsHTTPRequestJs2['default'].INIT_SEGMENT_TYPE;
    }

    function reset() {
        eventBus.off(_coreEventsEventsJs2['default'].FRAGMENT_LOADING_COMPLETED, onFragmentLoadingCompleted, this);
        fragmentModels = [];
    }

    function findModel(scheduleController) {
        var ln = fragmentModels.length;
        // We expect one-to-one relation between FragmentModel and context,
        // so just compare the given context object with the one that stored in the model to find the model for it
        for (var i = 0; i < ln; i++) {
            if (fragmentModels[i].getScheduleController() == scheduleController) {
                return fragmentModels[i];
            }
        }

        return null;
    }

    function createDataChunk(bytes, request, streamId) {
        var chunk = new _voDataChunkJs2['default']();

        chunk.streamId = streamId;
        chunk.mediaInfo = request.mediaInfo;
        chunk.segmentType = request.type;
        chunk.start = request.startTime;
        chunk.duration = request.duration;
        chunk.end = chunk.start + chunk.duration;
        chunk.bytes = bytes;
        chunk.index = request.index;
        chunk.quality = request.quality;

        return chunk;
    }

    function onFragmentLoadingCompleted(e) {
        var scheduleController = e.sender.getScheduleController();
        if (!findModel(scheduleController)) return;

        var request = e.request;
        var bytes = e.response;
        var isInit = isInitializationRequest(request);
        var streamId = scheduleController.getStreamProcessor().getStreamInfo().id;
        var chunk = undefined;

        if (!bytes) {
            log('No ' + request.mediaType + ' bytes to push.');
            return;
        }

        chunk = createDataChunk(bytes, request, streamId);
        eventBus.trigger(isInit ? _coreEventsEventsJs2['default'].INIT_FRAGMENT_LOADED : _coreEventsEventsJs2['default'].MEDIA_FRAGMENT_LOADED, { chunk: chunk, fragmentModel: e.sender });
    }

    instance = {
        process: process,
        getModel: getModel,
        detachModel: detachModel,
        isInitializationRequest: isInitializationRequest,
        reset: reset
    };

    setup();

    return instance;
}

FragmentController.__dashjs_factory_name = 'FragmentController';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(FragmentController);
module.exports = exports['default'];

},{"../../core/Debug.js":5,"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../models/FragmentModel.js":62,"../models/MetricsModel.js":65,"../vo/DataChunk.js":99,"../vo/metrics/HTTPRequest.js":117}],52:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var _TextSourceBufferJs = _dereq_('../TextSourceBuffer.js');

var _TextSourceBufferJs2 = _interopRequireDefault(_TextSourceBufferJs);

var _utilsDOMStorageJs = _dereq_('../utils/DOMStorage.js');

var _utilsDOMStorageJs2 = _interopRequireDefault(_utilsDOMStorageJs);

var TRACK_SWITCH_MODE_NEVER_REPLACE = 'neverReplace';
var TRACK_SWITCH_MODE_ALWAYS_REPLACE = 'alwaysReplace';
var TRACK_SELECTION_MODE_HIGHEST_BITRATE = 'highestBitrate';
var TRACK_SELECTION_MODE_WIDEST_RANGE = 'widestRange';
var DEFAULT_INIT_TRACK_SELECTION_MODE = TRACK_SELECTION_MODE_HIGHEST_BITRATE;

function MediaController() {

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();
    var textSourceBuffer = (0, _TextSourceBufferJs2['default'])(context).getInstance();
    var domStorage = (0, _utilsDOMStorageJs2['default'])(context).getInstance();

    var instance = undefined,
        tracks = undefined,
        initialSettings = undefined,
        selectionMode = undefined,
        switchMode = undefined,
        errHandler = undefined;

    function initialize() {
        tracks = {};
        resetInitialSettings();
        resetSwitchMode();
    }

    /**
     * @param type
     * @param streamInfo
     * @memberof MediaController#
     */
    function checkInitialMediaSettingsForType(type, streamInfo) {
        var settings = getInitialSettings(type);
        var tracksForType = getTracksFor(type, streamInfo);
        var tracks = [];

        if (type === 'fragmentedText') {
            // Choose the first track
            setTrack(tracksForType[0]);
            return;
        }

        if (!settings) {
            settings = domStorage.getSavedMediaSettings(type);
            setInitialSettings(type, settings);
        }

        if (!tracksForType || tracksForType.length === 0) return;

        if (settings) {
            tracksForType.forEach(function (track) {
                if (matchSettings(settings, track)) {
                    tracks.push(track);
                }
            });
        }

        if (tracks.length === 0) {
            setTrack(selectInitialTrack(tracksForType));
        } else {
            if (tracks.length > 1) {
                setTrack(selectInitialTrack(tracks));
            } else {
                setTrack(tracks[0]);
            }
        }
    }

    /**
     * @param track
     * @returns {Boolean}
     * @memberof MediaController#
     */
    function addTrack(track) {
        var mediaType = track ? track.type : null;
        var streamId = track ? track.streamInfo.id : null;
        var initSettings = getInitialSettings(mediaType);

        if (!track || !isMultiTrackSupportedByType(mediaType)) return false;

        tracks[streamId] = tracks[streamId] || createTrackInfo();

        if (tracks[streamId][mediaType].list.indexOf(track) >= 0) return false;

        tracks[streamId][mediaType].list.push(track);

        if (initSettings && matchSettings(initSettings, track) && !getCurrentTrackFor(mediaType, track.streamInfo)) {
            setTrack(track);
        }

        return true;
    }

    /**
     * @param type
     * @param streamInfo
     * @returns {Array}
     * @memberof MediaController#
     */
    function getTracksFor(type, streamInfo) {
        if (!type || !streamInfo) return [];

        var id = streamInfo.id;

        if (!tracks[id] || !tracks[id][type]) return [];

        return tracks[id][type].list;
    }

    /**
     * @param type
     * @param streamInfo
     * @returns {Object}
     * @memberof MediaController#
     */
    function getCurrentTrackFor(type, streamInfo) {
        if (!type || !streamInfo) return null;

        return tracks[streamInfo.id][type].current;
    }

    /**
     * @param track
     * @returns {Boolean}
     * @memberof MediaController#
     */
    function isCurrentTrack(track) {
        var type = track.type;
        var id = track.streamInfo.id;

        return tracks[id] && tracks[id][type] && isTracksEqual(tracks[id][type].current, track);
    }

    /**
     * @param track
     * @memberof MediaController#
     */
    function setTrack(track) {
        if (!track) return;

        var type = track.type;
        var streamInfo = track.streamInfo;
        var id = streamInfo.id;
        var current = getCurrentTrackFor(type, streamInfo);

        if (!tracks[id] || !tracks[id][type] || current && isTracksEqual(track, current)) return;

        tracks[id][type].current = track;

        if (current) {
            eventBus.trigger(_coreEventsEventsJs2['default'].CURRENT_TRACK_CHANGED, { oldMediaInfo: current, newMediaInfo: track, switchMode: switchMode[type] });
        }

        var settings = extractSettings(track);

        if (!settings || !tracks[id][type].storeLastSettings) return;

        if (settings.roles) {
            settings.role = settings.roles[0];
            delete settings.roles;
        }

        if (settings.accessibility) {
            settings.accessibility = settings.accessibility[0];
        }

        if (settings.audioChannelConfiguration) {
            settings.audioChannelConfiguration = settings.audioChannelConfiguration[0];
        }

        domStorage.setSavedMediaSettings(type, settings);
    }

    /**
     * @param type
     * @param {Object}
     * @memberof MediaController#
     */
    function setInitialSettings(type, value) {
        if (!type || value === undefined) return;

        initialSettings[type] = value;
    }

    /**
     * @param type
     * @returns {Object}
     * @memberof MediaController#
     */
    function getInitialSettings(type) {
        if (!type) return null;

        return initialSettings[type];
    }

    /**
     * @param type
     * @param mode
     * @memberof MediaController#
     */
    function setSwitchMode(type, mode) {
        var isModeSupported = !!MediaController[mode];

        if (!isModeSupported) {
            log('track switch mode is not supported: ' + mode);
            return;
        }

        switchMode[type] = mode;
    }

    /**
     * @param type
     * @returns mode
     * @memberof MediaController#
     */
    function getSwitchMode(type) {
        return switchMode[type];
    }

    /**
     * @param mode
     * @memberof MediaController#
     */
    function setSelectionModeForInitialTrack(mode) {
        var isModeSupported = !!MediaController.trackSelectionModes[mode];

        if (!isModeSupported) {
            log('track selection mode is not supported: ' + mode);
            return;
        }
        selectionMode = mode;
    }

    /**
     * @returns mode
     * @memberof MediaController#
     */
    function getSelectionModeForInitialTrack() {
        return selectionMode || DEFAULT_INIT_TRACK_SELECTION_MODE;
    }

    /**
     * @param type
     * @returns {Boolean}
     * @memberof MediaController#
     */
    function isMultiTrackSupportedByType(type) {
        return type === 'audio' || type === 'video' || type === 'text' || type === 'fragmentedText';
    }

    /**
     * @param t1 first track to compare
     * @param t2 second track to compare
     * @returns {Boolean}
     * @memberof MediaController#
     */
    function isTracksEqual(t1, t2) {
        var sameId = t1.id === t2.id;
        var sameViewpoint = t1.viewpoint === t2.viewpoint;
        var sameLang = t1.lang === t2.lang;
        var sameRoles = t1.roles.toString() == t2.roles.toString();
        var sameAccessibility = t1.accessibility.toString() == t2.accessibility.toString();
        var sameAudioChannelConfiguration = t1.audioChannelConfiguration.toString() == t2.audioChannelConfiguration.toString();

        return sameId && sameViewpoint && sameLang && sameRoles && sameAccessibility && sameAudioChannelConfiguration;
    }

    function setConfig(config) {
        if (!config) return;

        if (config.errHandler) {
            errHandler = config.errHandler;
        }
    }

    /**
     * @memberof MediaController#
     */
    function reset() {
        initialize();
        textSourceBuffer.resetEmbedded();
    }

    function extractSettings(mediaInfo) {
        var settings = {
            lang: mediaInfo.lang,
            viewpoint: mediaInfo.viewpoint,
            roles: mediaInfo.roles,
            accessibility: mediaInfo.accessibility,
            audioChannelConfiguration: mediaInfo.audioChannelConfiguration
        };
        var notEmpty = settings.lang || settings.viewpoint || settings.role && settings.role.length > 0 || settings.accessibility && settings.accessibility.length > 0 || settings.audioChannelConfiguration && settings.audioChannelConfiguration.length > 0;

        return notEmpty ? settings : null;
    }

    function matchSettings(settings, track) {
        var matchLang = !settings.lang || settings.lang === track.lang;
        var matchViewPoint = !settings.viewpoint || settings.viewpoint === track.viewpoint;
        var matchRole = !settings.role || !!track.roles.filter(function (item) {
            return item === settings.role;
        })[0];
        var matchAccessibility = !settings.accessibility || !!track.accessibility.filter(function (item) {
            return item === settings.accessibility;
        })[0];
        var matchAudioChannelConfiguration = !settings.audioChannelConfiguration || !!track.audioChannelConfiguration.filter(function (item) {
            return item === settings.audioChannelConfiguration;
        })[0];

        return matchLang && matchViewPoint && matchRole && matchAccessibility && matchAudioChannelConfiguration;
    }

    function resetSwitchMode() {
        switchMode = {
            audio: TRACK_SWITCH_MODE_ALWAYS_REPLACE,
            video: TRACK_SWITCH_MODE_NEVER_REPLACE
        };
    }

    function resetInitialSettings() {
        initialSettings = {
            audio: null,
            video: null
        };
    }

    function selectInitialTrack(tracks) {
        var mode = getSelectionModeForInitialTrack();
        var tmpArr = [];
        var getTracksWithHighestBitrate = function getTracksWithHighestBitrate(trackArr) {
            var max = 0;
            var result = [];
            var tmp;

            trackArr.forEach(function (track) {
                tmp = Math.max.apply(Math, track.bitrateList.map(function (obj) {
                    return obj.bandwidth;
                }));

                if (tmp > max) {
                    max = tmp;
                    result = [track];
                } else if (tmp === max) {
                    result.push(track);
                }
            });

            return result;
        };
        var getTracksWithWidestRange = function getTracksWithWidestRange(trackArr) {
            var max = 0;
            var result = [];
            var tmp;

            trackArr.forEach(function (track) {
                tmp = track.representationCount;

                if (tmp > max) {
                    max = tmp;
                    result = [track];
                } else if (tmp === max) {
                    result.push(track);
                }
            });

            return result;
        };

        switch (mode) {
            case TRACK_SELECTION_MODE_HIGHEST_BITRATE:
                tmpArr = getTracksWithHighestBitrate(tracks);

                if (tmpArr.length > 1) {
                    tmpArr = getTracksWithWidestRange(tmpArr);
                }
                break;
            case TRACK_SELECTION_MODE_WIDEST_RANGE:
                tmpArr = getTracksWithWidestRange(tracks);

                if (tmpArr.length > 1) {
                    tmpArr = getTracksWithHighestBitrate(tracks);
                }
                break;
            default:
                log('track selection mode is not supported: ' + mode);
                break;
        }

        return tmpArr[0];
    }

    function createTrackInfo() {
        return {
            audio: {
                list: [],
                storeLastSettings: true,
                current: null
            },
            video: {
                list: [],
                storeLastSettings: true,
                current: null
            },
            text: {
                list: [],
                storeLastSettings: true,
                current: null
            },
            fragmentedText: {
                list: [],
                storeLastSettings: true,
                current: null
            }
        };
    }

    instance = {
        initialize: initialize,
        checkInitialMediaSettingsForType: checkInitialMediaSettingsForType,
        addTrack: addTrack,
        getTracksFor: getTracksFor,
        getCurrentTrackFor: getCurrentTrackFor,
        isCurrentTrack: isCurrentTrack,
        setTrack: setTrack,
        setInitialSettings: setInitialSettings,
        getInitialSettings: getInitialSettings,
        setSwitchMode: setSwitchMode,
        getSwitchMode: getSwitchMode,
        setSelectionModeForInitialTrack: setSelectionModeForInitialTrack,
        getSelectionModeForInitialTrack: getSelectionModeForInitialTrack,
        isMultiTrackSupportedByType: isMultiTrackSupportedByType,
        isTracksEqual: isTracksEqual,
        setConfig: setConfig,
        reset: reset
    };

    return instance;
}

MediaController.__dashjs_factory_name = 'MediaController';
var factory = _coreFactoryMakerJs2['default'].getSingletonFactory(MediaController);
factory.TRACK_SWITCH_MODE_NEVER_REPLACE = TRACK_SWITCH_MODE_NEVER_REPLACE;
factory.TRACK_SWITCH_MODE_ALWAYS_REPLACE = TRACK_SWITCH_MODE_ALWAYS_REPLACE;
factory.TRACK_SELECTION_MODE_HIGHEST_BITRATE = TRACK_SELECTION_MODE_HIGHEST_BITRATE;
factory.TRACK_SELECTION_MODE_WIDEST_RANGE = TRACK_SELECTION_MODE_WIDEST_RANGE;
factory.DEFAULT_INIT_TRACK_SELECTION_MODE = DEFAULT_INIT_TRACK_SELECTION_MODE;
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/Debug.js":5,"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../TextSourceBuffer.js":41,"../utils/DOMStorage.js":89}],53:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function MediaSourceController() {

    var instance = undefined;

    function createMediaSource() {

        var hasWebKit = ('WebKitMediaSource' in window);
        var hasMediaSource = ('MediaSource' in window);

        if (hasMediaSource) {
            return new MediaSource();
        } else if (hasWebKit) {
            return new WebKitMediaSource();
        }

        return null;
    }

    function attachMediaSource(source, videoModel) {

        var objectURL = window.URL.createObjectURL(source);

        videoModel.setSource(objectURL);

        return objectURL;
    }

    function detachMediaSource(videoModel) {
        videoModel.setSource(null);
    }

    function setDuration(source, value) {

        if (source.duration != value) source.duration = value;

        return source.duration;
    }

    function signalEndOfStream(source) {

        var buffers = source.sourceBuffers;
        var ln = buffers.length;
        var i = 0;

        if (source.readyState !== 'open') return;

        for (i; i < ln; i++) {
            if (buffers[i].updating) return;
            if (buffers[i].buffered.length === 0) return;
        }

        source.endOfStream();
    }

    instance = {
        createMediaSource: createMediaSource,
        attachMediaSource: attachMediaSource,
        detachMediaSource: detachMediaSource,
        setDuration: setDuration,
        signalEndOfStream: signalEndOfStream
    };

    return instance;
}

MediaSourceController.__dashjs_factory_name = 'MediaSourceController';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(MediaSourceController);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7}],54:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _BufferControllerJs = _dereq_('./BufferController.js');

var _BufferControllerJs2 = _interopRequireDefault(_BufferControllerJs);

var _modelsURIQueryAndFragmentModelJs = _dereq_('../models/URIQueryAndFragmentModel.js');

var _modelsURIQueryAndFragmentModelJs2 = _interopRequireDefault(_modelsURIQueryAndFragmentModelJs);

var _streamingModelsMediaPlayerModelJs = _dereq_('../../streaming/models/MediaPlayerModel.js');

var _streamingModelsMediaPlayerModelJs2 = _interopRequireDefault(_streamingModelsMediaPlayerModelJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

function PlaybackController() {

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        element = undefined,
        streamController = undefined,
        timelineConverter = undefined,
        metricsModel = undefined,
        dashMetrics = undefined,
        manifestModel = undefined,
        dashManifestModel = undefined,
        adapter = undefined,
        videoModel = undefined,
        currentTime = undefined,
        liveStartTime = undefined,
        wallclockTimeIntervalId = undefined,
        commonEarliestTime = undefined,
        streamInfo = undefined,
        isDynamic = undefined,
        mediaPlayerModel = undefined,
        playOnceInitialized = undefined;

    function setup() {
        currentTime = 0;
        liveStartTime = NaN;
        wallclockTimeIntervalId = null;
        isDynamic = null;
        playOnceInitialized = false;
        commonEarliestTime = {};
        mediaPlayerModel = (0, _streamingModelsMediaPlayerModelJs2['default'])(context).getInstance();
    }

    function initialize(StreamInfo) {
        streamInfo = StreamInfo;
        element = videoModel.getElement();
        addAllListeners();
        isDynamic = streamInfo.manifestInfo.isDynamic;
        liveStartTime = streamInfo.start;
        eventBus.on(_coreEventsEventsJs2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this);
        eventBus.on(_coreEventsEventsJs2['default'].LIVE_EDGE_SEARCH_COMPLETED, onLiveEdgeSearchCompleted, this);
        eventBus.on(_coreEventsEventsJs2['default'].BYTES_APPENDED, onBytesAppended, this);
        eventBus.on(_coreEventsEventsJs2['default'].BUFFER_LEVEL_STATE_CHANGED, onBufferLevelStateChanged, this);
        eventBus.on(_coreEventsEventsJs2['default'].PERIOD_SWITCH_STARTED, onPeriodSwitchStarted, this);

        if (playOnceInitialized) {
            playOnceInitialized = false;
            play();
        }
    }

    function onPeriodSwitchStarted(e) {
        if (e.fromStreamInfo && commonEarliestTime[e.fromStreamInfo.id]) {
            delete commonEarliestTime[e.fromStreamInfo.id];
        }
    }

    function getTimeToStreamEnd() {
        return getStreamStartTime(true) + streamInfo.duration - getTime();
    }

    function isPlaybackStarted() {
        return getTime() > 0;
    }

    function getStreamId() {
        return streamInfo.id;
    }

    function getStreamDuration() {
        return streamInfo.duration;
    }

    function play() {
        if (element) {
            element.autoplay = true;
            element.play();
        } else {
            playOnceInitialized = true;
        }
    }

    function isPaused() {
        if (!element) return;
        return element.paused;
    }

    function pause() {
        if (!element) return;
        element.pause();
        element.autoplay = false;
    }

    function isSeeking() {
        if (!element) return;
        return element.seeking;
    }

    function seek(time) {
        if (!videoModel) return;
        log('Requesting seek to time: ' + time);
        videoModel.setCurrentTime(time);
    }

    function getTime() {
        if (!element) return;
        return element.currentTime;
    }

    function getPlaybackRate() {
        if (!element) return;
        return element.playbackRate;
    }

    function getPlayedRanges() {
        if (!element) return;
        return element.played;
    }

    function getEnded() {
        if (!element) return;
        return element.ended;
    }

    function getIsDynamic() {
        return isDynamic;
    }

    function setLiveStartTime(value) {
        liveStartTime = value;
    }

    function getLiveStartTime() {
        return liveStartTime;
    }

    /**
     * Computes the desirable delay for the live edge to avoid a risk of getting 404 when playing at the bleeding edge
     * @returns {Number} object
     * @memberof PlaybackController#
     * */
    function computeLiveDelay(fragmentDuration, dvrWindowSize) {
        var mpd = dashManifestModel.getMpd(manifestModel.getValue());

        var delay = undefined;
        var END_OF_PLAYLIST_PADDING = 10;

        if (mediaPlayerModel.getUseSuggestedPresentationDelay() && mpd.hasOwnProperty('suggestedPresentationDelay')) {
            delay = mpd.suggestedPresentationDelay;
        } else if (mediaPlayerModel.getLiveDelay()) {
            delay = mediaPlayerModel.getLiveDelay(); // If set by user, this value takes precedence
        } else if (!isNaN(fragmentDuration)) {
                delay = fragmentDuration * mediaPlayerModel.getLiveDelayFragmentCount();
            } else {
                delay = streamInfo.manifestInfo.minBufferTime * 2;
            }

        // cap target latency to:
        // - dvrWindowSize / 2 for short playlists
        // - dvrWindowSize - END_OF_PLAYLIST_PADDING for longer playlists
        var targetDelayCapping = Math.max(dvrWindowSize - END_OF_PLAYLIST_PADDING, dvrWindowSize / 2);

        return Math.min(delay, targetDelayCapping);
    }

    function reset() {
        if (videoModel && element) {
            eventBus.off(_coreEventsEventsJs2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this);
            eventBus.off(_coreEventsEventsJs2['default'].BUFFER_LEVEL_STATE_CHANGED, onBufferLevelStateChanged, this);
            eventBus.off(_coreEventsEventsJs2['default'].LIVE_EDGE_SEARCH_COMPLETED, onLiveEdgeSearchCompleted, this);
            eventBus.off(_coreEventsEventsJs2['default'].BYTES_APPENDED, onBytesAppended, this);
            stopUpdatingWallclockTime();
            removeAllListeners();
        }
        videoModel = null;
        streamInfo = null;
        element = null;
        isDynamic = null;
        setup();
    }

    function setConfig(config) {
        if (!config) return;

        if (config.streamController) {
            streamController = config.streamController;
        }
        if (config.timelineConverter) {
            timelineConverter = config.timelineConverter;
        }
        if (config.metricsModel) {
            metricsModel = config.metricsModel;
        }
        if (config.dashMetrics) {
            dashMetrics = config.dashMetrics;
        }
        if (config.manifestModel) {
            manifestModel = config.manifestModel;
        }
        if (config.dashManifestModel) {
            dashManifestModel = config.dashManifestModel;
        }
        if (config.adapter) {
            adapter = config.adapter;
        }
        if (config.videoModel) {
            videoModel = config.videoModel;
        }
    }

    /**
     * @param streamInfo object
     * @returns {Number} object
     * @memberof PlaybackController#
     */
    function getStreamStartTime(ignoreStartOffset) {
        var presentationStartTime = undefined;
        var startTimeOffset = !ignoreStartOffset ? parseInt((0, _modelsURIQueryAndFragmentModelJs2['default'])(context).getInstance().getURIFragmentData().s, 10) : NaN;

        if (isDynamic) {
            if (!isNaN(startTimeOffset) && startTimeOffset > 1262304000) {

                presentationStartTime = startTimeOffset - streamInfo.manifestInfo.availableFrom.getTime() / 1000;

                if (presentationStartTime > liveStartTime || presentationStartTime < liveStartTime - streamInfo.manifestInfo.DVRWindowSize) {
                    presentationStartTime = null;
                }
            }
            presentationStartTime = presentationStartTime || liveStartTime;
        } else {
            if (!isNaN(startTimeOffset) && startTimeOffset < Math.max(streamInfo.manifestInfo.duration, streamInfo.duration) && startTimeOffset >= 0) {
                presentationStartTime = startTimeOffset;
            } else {
                var earliestTime = commonEarliestTime[streamInfo.id]; //set by ready bufferStart after first onBytesAppended
                if (earliestTime === undefined) {
                    earliestTime = streamController.getActiveStreamCommonEarliestTime(); //deal with calculated PST that is none 0 when streamInfo.start is 0
                }
                presentationStartTime = Math.max(earliestTime, streamInfo.start);
            }
        }

        return presentationStartTime;
    }

    function getActualPresentationTime(currentTime) {
        var metrics = metricsModel.getReadOnlyMetricsFor('video') || metricsModel.getReadOnlyMetricsFor('audio');
        var DVRMetrics = dashMetrics.getCurrentDVRInfo(metrics);
        var DVRWindow = DVRMetrics ? DVRMetrics.range : null;
        var actualTime;

        if (!DVRWindow) return NaN;

        if (currentTime >= DVRWindow.start && currentTime <= DVRWindow.end) {
            return currentTime;
        }

        actualTime = Math.max(DVRWindow.end - streamInfo.manifestInfo.minBufferTime * 2, DVRWindow.start);

        return actualTime;
    }

    function startUpdatingWallclockTime() {
        if (wallclockTimeIntervalId !== null) return;

        var tick = function tick() {
            onWallclockTime();
        };

        wallclockTimeIntervalId = setInterval(tick, mediaPlayerModel.getWallclockTimeUpdateInterval());
    }

    function stopUpdatingWallclockTime() {
        clearInterval(wallclockTimeIntervalId);
        wallclockTimeIntervalId = null;
    }

    function seekToStartTimeOffset() {
        var initialSeekTime = getStreamStartTime(false);
        if (initialSeekTime > 0) {
            seek(initialSeekTime);
            log('Starting playback at offset: ' + initialSeekTime);
        }
    }

    function updateCurrentTime() {
        if (isPaused() || !isDynamic || element.readyState === 0) return;
        var currentTime = getTime();
        var actualTime = getActualPresentationTime(currentTime);
        var timeChanged = !isNaN(actualTime) && actualTime !== currentTime;
        if (timeChanged) {
            seek(actualTime);
        }
    }

    function onDataUpdateCompleted(e) {
        if (e.error) return;

        var representationInfo = adapter.convertDataToTrack(manifestModel.getValue(), e.currentRepresentation);
        var info = representationInfo.mediaInfo.streamInfo;

        if (streamInfo.id !== info.id) return;
        streamInfo = info;

        updateCurrentTime();
    }

    function onLiveEdgeSearchCompleted(e) {
        //This is here to catch the case when live edge search takes longer than playback metadata
        if (e.error || element.readyState === 0) return;
        seekToStartTimeOffset();
    }

    function onCanPlay() {
        eventBus.trigger(_coreEventsEventsJs2['default'].CAN_PLAY);
    }

    function onPlaybackStart() {
        log('Native video element event: play');
        updateCurrentTime();
        startUpdatingWallclockTime();
        eventBus.trigger(_coreEventsEventsJs2['default'].PLAYBACK_STARTED, { startTime: getTime() });
    }

    function onPlaybackPlaying() {
        log('Native video element event: playing');
        eventBus.trigger(_coreEventsEventsJs2['default'].PLAYBACK_PLAYING, { playingTime: getTime() });
    }

    function onPlaybackPaused() {
        log('Native video element event: pause');
        eventBus.trigger(_coreEventsEventsJs2['default'].PLAYBACK_PAUSED, { ended: getEnded() });
    }

    function onPlaybackSeeking() {
        var seekTime = getTime();
        log('Seeking to: ' + seekTime);
        startUpdatingWallclockTime();
        eventBus.trigger(_coreEventsEventsJs2['default'].PLAYBACK_SEEKING, { seekTime: seekTime });
    }

    function onPlaybackSeeked() {
        log('Native video element event: seeked');
        eventBus.trigger(_coreEventsEventsJs2['default'].PLAYBACK_SEEKED);
    }

    function onPlaybackTimeUpdated() {
        //log("Native video element event: timeupdate");
        var time = getTime();
        if (time === currentTime) return;
        currentTime = time;
        eventBus.trigger(_coreEventsEventsJs2['default'].PLAYBACK_TIME_UPDATED, { timeToEnd: getTimeToStreamEnd(), time: time });
    }

    function onPlaybackProgress() {
        //log("Native video element event: progress");
        eventBus.trigger(_coreEventsEventsJs2['default'].PLAYBACK_PROGRESS);
    }

    function onPlaybackRateChanged() {
        var rate = getPlaybackRate();
        log('Native video element event: ratechange: ', rate);
        eventBus.trigger(_coreEventsEventsJs2['default'].PLAYBACK_RATE_CHANGED, { playbackRate: rate });
    }

    function onPlaybackMetaDataLoaded() {
        log('Native video element event: loadedmetadata');
        if (!isDynamic && streamInfo.isFirst || timelineConverter.isTimeSyncCompleted()) {
            seekToStartTimeOffset();
        }
        eventBus.trigger(_coreEventsEventsJs2['default'].PLAYBACK_METADATA_LOADED);
        startUpdatingWallclockTime();
    }

    function onPlaybackEnded() {
        log('Native video element event: ended');
        pause();
        stopUpdatingWallclockTime();
        eventBus.trigger(_coreEventsEventsJs2['default'].PLAYBACK_ENDED);
    }

    function onPlaybackError(event) {
        var target = event.target || event.srcElement;
        eventBus.trigger(_coreEventsEventsJs2['default'].PLAYBACK_ERROR, { error: target.error });
    }

    function onWallclockTime() {
        eventBus.trigger(_coreEventsEventsJs2['default'].WALLCLOCK_TIME_UPDATED, { isDynamic: isDynamic, time: new Date() });
    }

    function onBytesAppended(e) {
        var ranges = e.bufferedRanges;
        if (!ranges || !ranges.length) return;
        var bufferedStart = Math.max(ranges.start(0), streamInfo.start);
        var earliestTime = commonEarliestTime[streamInfo.id] === undefined ? bufferedStart : Math.max(commonEarliestTime[streamInfo.id], bufferedStart);
        if (earliestTime === commonEarliestTime[streamInfo.id]) return;
        if (!isDynamic && !streamInfo.isFirst && getStreamStartTime(true) < earliestTime) {
            seek(earliestTime);
        }
        commonEarliestTime[streamInfo.id] = earliestTime;
    }

    function onBufferLevelStateChanged(e) {
        // do not stall playback when get an event from Stream that is not active
        if (e.streamInfo.id !== streamInfo.id) return;
        videoModel.setStallState(e.mediaType, e.state === _BufferControllerJs2['default'].BUFFER_EMPTY);
    }

    function addAllListeners() {
        element.addEventListener('canplay', onCanPlay);
        element.addEventListener('play', onPlaybackStart);
        element.addEventListener('playing', onPlaybackPlaying);
        element.addEventListener('pause', onPlaybackPaused);
        element.addEventListener('error', onPlaybackError);
        element.addEventListener('seeking', onPlaybackSeeking);
        element.addEventListener('seeked', onPlaybackSeeked);
        element.addEventListener('timeupdate', onPlaybackTimeUpdated);
        element.addEventListener('progress', onPlaybackProgress);
        element.addEventListener('ratechange', onPlaybackRateChanged);
        element.addEventListener('loadedmetadata', onPlaybackMetaDataLoaded);
        element.addEventListener('ended', onPlaybackEnded);
    }

    function removeAllListeners() {
        element.removeEventListener('canplay', onCanPlay);
        element.removeEventListener('play', onPlaybackStart);
        element.removeEventListener('playing', onPlaybackPlaying);
        element.removeEventListener('pause', onPlaybackPaused);
        element.removeEventListener('error', onPlaybackError);
        element.removeEventListener('seeking', onPlaybackSeeking);
        element.removeEventListener('seeked', onPlaybackSeeked);
        element.removeEventListener('timeupdate', onPlaybackTimeUpdated);
        element.removeEventListener('progress', onPlaybackProgress);
        element.removeEventListener('ratechange', onPlaybackRateChanged);
        element.removeEventListener('loadedmetadata', onPlaybackMetaDataLoaded);
        element.removeEventListener('ended', onPlaybackEnded);
    }

    instance = {
        initialize: initialize,
        setConfig: setConfig,
        getStreamStartTime: getStreamStartTime,
        getTimeToStreamEnd: getTimeToStreamEnd,
        isPlaybackStarted: isPlaybackStarted,
        getStreamId: getStreamId,
        getStreamDuration: getStreamDuration,
        getTime: getTime,
        getPlaybackRate: getPlaybackRate,
        getPlayedRanges: getPlayedRanges,
        getEnded: getEnded,
        getIsDynamic: getIsDynamic,
        setLiveStartTime: setLiveStartTime,
        getLiveStartTime: getLiveStartTime,
        computeLiveDelay: computeLiveDelay,
        play: play,
        isPaused: isPaused,
        pause: pause,
        isSeeking: isSeeking,
        seek: seek,
        reset: reset
    };

    setup();

    return instance;
}

PlaybackController.__dashjs_factory_name = 'PlaybackController';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(PlaybackController);
module.exports = exports['default'];

},{"../../core/Debug.js":5,"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../../streaming/models/MediaPlayerModel.js":64,"../models/URIQueryAndFragmentModel.js":66,"./BufferController.js":49}],55:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _voMetricsPlayListJs = _dereq_('../vo/metrics/PlayList.js');

var _voMetricsPlayListJs2 = _interopRequireDefault(_voMetricsPlayListJs);

var _PlaybackControllerJs = _dereq_('./PlaybackController.js');

var _PlaybackControllerJs2 = _interopRequireDefault(_PlaybackControllerJs);

var _AbrControllerJs = _dereq_('./AbrController.js');

var _AbrControllerJs2 = _interopRequireDefault(_AbrControllerJs);

var _BufferControllerJs = _dereq_('./BufferController.js');

var _BufferControllerJs2 = _interopRequireDefault(_BufferControllerJs);

var _rulesSchedulingBufferLevelRuleJs = _dereq_('../rules/scheduling/BufferLevelRule.js');

var _rulesSchedulingBufferLevelRuleJs2 = _interopRequireDefault(_rulesSchedulingBufferLevelRuleJs);

var _rulesSchedulingNextFragmentRequestRuleJs = _dereq_('../rules/scheduling/NextFragmentRequestRule.js');

var _rulesSchedulingNextFragmentRequestRuleJs2 = _interopRequireDefault(_rulesSchedulingNextFragmentRequestRuleJs);

var _TextSourceBufferJs = _dereq_('../TextSourceBuffer.js');

var _TextSourceBufferJs2 = _interopRequireDefault(_TextSourceBufferJs);

var _modelsMetricsModelJs = _dereq_('../models/MetricsModel.js');

var _modelsMetricsModelJs2 = _interopRequireDefault(_modelsMetricsModelJs);

var _dashDashMetricsJs = _dereq_('../../dash/DashMetrics.js');

var _dashDashMetricsJs2 = _interopRequireDefault(_dashDashMetricsJs);

var _dashDashAdapterJs = _dereq_('../../dash/DashAdapter.js');

var _dashDashAdapterJs2 = _interopRequireDefault(_dashDashAdapterJs);

var _controllersSourceBufferControllerJs = _dereq_('../controllers/SourceBufferController.js');

var _controllersSourceBufferControllerJs2 = _interopRequireDefault(_controllersSourceBufferControllerJs);

var _VirtualBufferJs = _dereq_('../VirtualBuffer.js');

var _VirtualBufferJs2 = _interopRequireDefault(_VirtualBufferJs);

var _utilsLiveEdgeFinderJs = _dereq_('../utils/LiveEdgeFinder.js');

var _utilsLiveEdgeFinderJs2 = _interopRequireDefault(_utilsLiveEdgeFinderJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

function ScheduleController(config) {

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var metricsModel = config.metricsModel;
    var manifestModel = config.manifestModel;
    var adapter = config.adapter;
    var dashMetrics = config.dashMetrics;
    var dashManifestModel = config.dashManifestModel;
    var timelineConverter = config.timelineConverter;
    var mediaPlayerModel = config.mediaPlayerModel;

    var instance = undefined,
        type = undefined,
        ready = undefined,
        fragmentModel = undefined,
        isDynamic = undefined,
        currentRepresentationInfo = undefined,
        initialPlayback = undefined,
        isStopped = undefined,
        playListMetrics = undefined,
        playListTraceMetrics = undefined,
        playListTraceMetricsClosed = undefined,
        isFragmentLoading = undefined,
        timeToLoadDelay = undefined,
        validateTimeout = undefined,
        seekTarget = undefined,
        playbackController = undefined,
        abrController = undefined,
        streamProcessor = undefined,
        fragmentController = undefined,
        liveEdgeFinder = undefined,
        bufferController = undefined,
        bufferLevelRule = undefined,
        nextFragmentRequestRule = undefined,
        scheduleWhilePaused = undefined;

    function setup() {
        initialPlayback = true;
        isStopped = false;
        playListMetrics = null;
        playListTraceMetrics = null;
        playListTraceMetricsClosed = true;
        isFragmentLoading = false;
        timeToLoadDelay = 0;
        seekTarget = NaN;
    }

    function initialize(Type, StreamProcessor) {
        type = Type;
        streamProcessor = StreamProcessor;
        liveEdgeFinder = (0, _utilsLiveEdgeFinderJs2['default'])(context).getInstance();
        playbackController = (0, _PlaybackControllerJs2['default'])(context).getInstance();
        abrController = (0, _AbrControllerJs2['default'])(context).getInstance();
        fragmentController = streamProcessor.getFragmentController();
        bufferController = streamProcessor.getBufferController();
        fragmentModel = fragmentController.getModel(this);
        isDynamic = streamProcessor.isDynamic();
        scheduleWhilePaused = mediaPlayerModel.getScheduleWhilePaused();

        bufferLevelRule = (0, _rulesSchedulingBufferLevelRuleJs2['default'])(context).create({
            dashMetrics: (0, _dashDashMetricsJs2['default'])(context).getInstance(),
            metricsModel: (0, _modelsMetricsModelJs2['default'])(context).getInstance(),
            textSourceBuffer: (0, _TextSourceBufferJs2['default'])(context).getInstance()
        });

        nextFragmentRequestRule = (0, _rulesSchedulingNextFragmentRequestRuleJs2['default'])(context).create({
            adapter: (0, _dashDashAdapterJs2['default'])(context).getInstance(),
            sourceBufferController: (0, _controllersSourceBufferControllerJs2['default'])(context).getInstance(),
            virtualBuffer: (0, _VirtualBufferJs2['default'])(context).getInstance(),
            textSourceBuffer: (0, _TextSourceBufferJs2['default'])(context).getInstance()

        });

        if (dashManifestModel.getIsTextTrack(type)) {
            eventBus.on(_coreEventsEventsJs2['default'].TIMED_TEXT_REQUESTED, onTimedTextRequested, this);
        }

        eventBus.on(_coreEventsEventsJs2['default'].LIVE_EDGE_SEARCH_COMPLETED, onLiveEdgeSearchCompleted, this);
        eventBus.on(_coreEventsEventsJs2['default'].QUALITY_CHANGED, onQualityChanged, this);
        eventBus.on(_coreEventsEventsJs2['default'].DATA_UPDATE_STARTED, onDataUpdateStarted, this);
        eventBus.on(_coreEventsEventsJs2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this);
        eventBus.on(_coreEventsEventsJs2['default'].FRAGMENT_LOADING_COMPLETED, onFragmentLoadingCompleted, this);
        eventBus.on(_coreEventsEventsJs2['default'].STREAM_COMPLETED, onStreamCompleted, this);
        eventBus.on(_coreEventsEventsJs2['default'].STREAM_INITIALIZED, onStreamInitialized, this);
        eventBus.on(_coreEventsEventsJs2['default'].BUFFER_LEVEL_STATE_CHANGED, onBufferLevelStateChanged, this);
        eventBus.on(_coreEventsEventsJs2['default'].BUFFER_CLEARED, onBufferCleared, this);
        eventBus.on(_coreEventsEventsJs2['default'].BYTES_APPENDED, onBytesAppended, this);
        eventBus.on(_coreEventsEventsJs2['default'].INIT_REQUESTED, onInitRequested, this);
        eventBus.on(_coreEventsEventsJs2['default'].QUOTA_EXCEEDED, onQuotaExceeded, this);
        eventBus.on(_coreEventsEventsJs2['default'].BUFFER_LEVEL_STATE_CHANGED, onBufferLevelStateChanged, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_STARTED, onPlaybackStarted, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_RATE_CHANGED, onPlaybackRateChanged, this);
        eventBus.on(_coreEventsEventsJs2['default'].URL_RESOLUTION_FAILED, onURLResolutionFailed, this);
    }

    function clearPlayListTraceMetrics(endTime, stopreason) {
        var duration = 0;
        var startTime = null;

        if (playListMetrics && playListTraceMetricsClosed === false) {
            startTime = playListTraceMetrics.start;
            duration = endTime.getTime() - startTime.getTime();

            playListTraceMetrics.duration = duration;
            playListTraceMetrics.stopreason = stopreason;

            playListMetrics.trace.push(playListTraceMetrics);

            playListTraceMetricsClosed = true;
        }
    }

    function start() {
        if (!ready) return;
        addPlaylistTraceMetrics();
        isStopped = false;

        if (initialPlayback) {
            getInitRequest(currentRepresentationInfo.quality);
        } else {
            //Validate will be first called after the init segment is appended. But in the case where we stop and start
            //the ScheduleController E.g dateUpdate on manifest refresh for live streams. we need to start validate again.
            validate();
        }

        if (initialPlayback) {
            initialPlayback = false;
        }
        log('Schedule controller starting for ' + type);
    }

    function stop() {
        if (isStopped) return;
        isStopped = true;
        clearInterval(validateTimeout);
        log('Schedule controller stopping for ' + type);
    }

    function getInitRequest(quality) {
        var request = adapter.getInitRequest(streamProcessor, quality);

        if (request !== null) {
            fragmentModel.executeRequest(request);
        }

        return request;
    }

    function replaceCanceledRequests(canceledRequests) {
        var ln = canceledRequests.length;
        // EPSILON is used to avoid javascript floating point issue, e.g. if request.startTime = 19.2,
        // request.duration = 3.83, than request.startTime + request.startTime = 19.2 + 1.92 = 21.119999999999997
        var EPSILON = 0.1;
        var request, time, i;

        for (i = 0; i < ln; i++) {
            request = canceledRequests[i];
            time = request.startTime + request.duration / 2 + EPSILON;
            request = adapter.getFragmentRequestForTime(streamProcessor, currentRepresentationInfo, time, { timeThreshold: 0, ignoreIsFinished: true });
            if (request) {
                fragmentModel.executeRequest(request);
            }
        }
    }

    function validate() {
        if (isStopped || playbackController.isPaused() && !scheduleWhilePaused) return;
        //log("validating", type);
        var readyToLoad = bufferLevelRule.execute(streamProcessor);
        if (readyToLoad && !isFragmentLoading && (dashManifestModel.getIsTextTrack(type) || !bufferController.getIsAppendingInProgress())) {
            isFragmentLoading = true;

            var getNextFragment = function getNextFragment() {
                var request = nextFragmentRequestRule.execute(streamProcessor);
                if (request) {
                    fragmentModel.executeRequest(request); // we load
                } else {
                        isFragmentLoading = false;
                        startValidateTimer(1000); //we loop
                    }
            };
            //Run ABR rules - let it callback to getNextFragment once it is done running.
            abrController.getPlaybackQuality(streamProcessor, getNextFragment);
        } else {
            startValidateTimer(1000); //we loop
        }
    }

    function startValidateTimer(value) {
        validateTimeout = setTimeout(validate, value);
    }

    function onQualityChanged(e) {
        if (type !== e.mediaType || streamProcessor.getStreamInfo().id !== e.streamInfo.id) return;

        currentRepresentationInfo = streamProcessor.getRepresentationInfoForQuality(e.newQuality);
        if (currentRepresentationInfo === null || currentRepresentationInfo === undefined) {
            throw 'Unexpected error! - currentRepresentationInfo is null or undefined';
        }

        clearPlayListTraceMetrics(new Date(), _voMetricsPlayListJs2['default'].Trace.REPRESENTATION_SWITCH_STOP_REASON);
        addPlaylistTraceMetrics();
    }

    function onDataUpdateCompleted(e) {
        if (e.error) return;
        currentRepresentationInfo = adapter.convertDataToTrack(manifestModel.getValue(), e.currentRepresentation);
    }

    function onStreamInitialized(e) {
        if (e.error) return;

        currentRepresentationInfo = streamProcessor.getCurrentRepresentationInfo();

        if (!isDynamic || liveEdgeFinder.getLiveEdge() !== null) {
            ready = true;
        }

        start();
    }

    function onStreamCompleted(e) {
        if (e.fragmentModel !== fragmentModel) return;
        stop();
        log('Stream is complete');
    }

    function onFragmentLoadingCompleted(e) {
        if (e.sender !== fragmentModel) return;

        if (!isNaN(e.request.index)) {
            isFragmentLoading = false;
        }

        if (e.error && e.serviceLocation && !isStopped) {
            replaceCanceledRequests([e.request]);
        }
    }

    function onBytesAppended(e) {
        if (e.sender.getStreamProcessor() !== streamProcessor) return;
        validate();
    }

    function onDataUpdateStarted(e) {
        if (e.sender.getStreamProcessor() !== streamProcessor) return;
        stop();
    }

    function onInitRequested(e) {
        if (e.sender.getStreamProcessor() !== streamProcessor) return;

        getInitRequest(e.requiredQuality);
    }

    function onBufferCleared(e) {
        if (e.sender.getStreamProcessor() !== streamProcessor) return;
        // after the data has been removed from the buffer we should remove the requests from the list of
        // the executed requests for which playback time is inside the time interval that has been removed from the buffer
        fragmentModel.removeExecutedRequestsBeforeTime(e.to);

        if (e.hasEnoughSpaceToAppend && !bufferController.getIsBufferingCompleted()) {
            start();
        }
    }

    function onBufferLevelStateChanged(e) {
        if (e.sender.getStreamProcessor() === streamProcessor && e.state === _BufferControllerJs2['default'].BUFFER_EMPTY && !playbackController.isSeeking()) {
            log('Stalling Buffer');
            clearPlayListTraceMetrics(new Date(), _voMetricsPlayListJs2['default'].Trace.REBUFFERING_REASON);
        }
    }

    function onQuotaExceeded(e) {
        if (e.sender.getStreamProcessor() !== streamProcessor) return;
        stop();
    }

    function onURLResolutionFailed() {
        fragmentModel.abortRequests();
        stop();
    }

    function addPlaylistTraceMetrics() {
        if (playListMetrics && playListTraceMetricsClosed === true && currentRepresentationInfo) {
            playListTraceMetricsClosed = false;

            playListTraceMetrics = new _voMetricsPlayListJs2['default'].Trace();
            playListTraceMetrics.representationid = currentRepresentationInfo.id;
            playListTraceMetrics.start = new Date();
            playListTraceMetrics.mstart = playbackController.getTime() * 1000;
            playListTraceMetrics.playbackspeed = playbackController.getPlaybackRate().toString();
        }
    }

    function onTimedTextRequested(e) {
        if (e.sender.getStreamProcessor() !== streamProcessor) return;
        getInitRequest(e.index);
    }

    function onPlaybackStarted() {
        start();
    }

    function onPlaybackSeeking(e) {

        seekTarget = e.seekTime;

        if (!initialPlayback) {
            isFragmentLoading = false;
        }
        if (isStopped) {
            start();
        }

        var metrics = metricsModel.getMetricsFor('stream');
        var manifestUpdateInfo = dashMetrics.getCurrentManifestUpdate(metrics);
        var latency = currentRepresentationInfo.DVRWindow ? currentRepresentationInfo.DVRWindow.end - playbackController.getTime() : NaN;
        metricsModel.updateManifestUpdateInfo(manifestUpdateInfo, { latency: latency });
    }

    function onPlaybackRateChanged(e) {
        if (playListTraceMetrics) {
            playListTraceMetrics.playbackspeed = e.playbackRate.toString();
        }
    }

    function onLiveEdgeSearchCompleted(e) {
        if (e.error) return;

        var liveEdgeTime = e.liveEdge;
        var manifestInfo = currentRepresentationInfo.mediaInfo.streamInfo.manifestInfo;
        var startTime = liveEdgeTime - playbackController.computeLiveDelay(currentRepresentationInfo.fragmentDuration, manifestInfo.DVRWindowSize / 2);
        var metrics = metricsModel.getMetricsFor('stream');
        var manifestUpdateInfo = dashMetrics.getCurrentManifestUpdate(metrics);
        var currentLiveStart = playbackController.getLiveStartTime();

        var request = undefined,
            actualStartTime = undefined;

        // get a request for a start time
        request = adapter.getFragmentRequestForTime(streamProcessor, currentRepresentationInfo, startTime, { ignoreIsFinished: true });
        actualStartTime = request.startTime;
        seekTarget = actualStartTime; //Setting seekTarget will allow NextFragmentRequestRule's first request time to be accurate.
        if (isNaN(currentLiveStart) || actualStartTime > currentLiveStart) {
            playbackController.setLiveStartTime(actualStartTime);
        }

        metricsModel.updateManifestUpdateInfo(manifestUpdateInfo, { currentTime: actualStartTime, presentationStartTime: liveEdgeTime, latency: liveEdgeTime - actualStartTime, clientTimeOffset: timelineConverter.getClientTimeOffset() });
        ready = true;
        start();
    }

    function getSeekTarget() {
        return seekTarget;
    }

    function setSeekTarget(value) {
        seekTarget = value;
    }

    function getFragmentModel() {
        return fragmentModel;
    }

    function setTimeToLoadDelay(value) {
        timeToLoadDelay = value;
    }

    function getTimeToLoadDelay() {
        return timeToLoadDelay;
    }

    function getStreamProcessor() {
        return streamProcessor;
    }

    function setPlayList(playList) {
        playListMetrics = playList;
    }

    function finalisePlayList(time, reason) {
        clearPlayListTraceMetrics(time, reason);
        playListMetrics = null;
    }

    function reset() {
        eventBus.off(_coreEventsEventsJs2['default'].LIVE_EDGE_SEARCH_COMPLETED, onLiveEdgeSearchCompleted, this);
        eventBus.off(_coreEventsEventsJs2['default'].DATA_UPDATE_STARTED, onDataUpdateStarted, this);
        eventBus.off(_coreEventsEventsJs2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this);
        eventBus.off(_coreEventsEventsJs2['default'].BUFFER_LEVEL_STATE_CHANGED, onBufferLevelStateChanged, this);
        eventBus.off(_coreEventsEventsJs2['default'].QUALITY_CHANGED, onQualityChanged, this);
        eventBus.off(_coreEventsEventsJs2['default'].FRAGMENT_LOADING_COMPLETED, onFragmentLoadingCompleted, this);
        eventBus.off(_coreEventsEventsJs2['default'].STREAM_COMPLETED, onStreamCompleted, this);
        eventBus.off(_coreEventsEventsJs2['default'].STREAM_INITIALIZED, onStreamInitialized, this);
        eventBus.off(_coreEventsEventsJs2['default'].QUOTA_EXCEEDED, onQuotaExceeded, this);
        eventBus.off(_coreEventsEventsJs2['default'].BYTES_APPENDED, onBytesAppended, this);
        eventBus.off(_coreEventsEventsJs2['default'].BUFFER_CLEARED, onBufferCleared, this);
        eventBus.off(_coreEventsEventsJs2['default'].INIT_REQUESTED, onInitRequested, this);
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_RATE_CHANGED, onPlaybackRateChanged, this);
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, this);
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_STARTED, onPlaybackStarted, this);
        eventBus.off(_coreEventsEventsJs2['default'].URL_RESOLUTION_FAILED, onURLResolutionFailed, this);

        if (dashManifestModel.getIsTextTrack(type)) {
            eventBus.off(_coreEventsEventsJs2['default'].TIMED_TEXT_REQUESTED, onTimedTextRequested, this);
        }

        stop();
        fragmentController.detachModel(fragmentModel);
        isFragmentLoading = false;
        timeToLoadDelay = 0;
        seekTarget = NaN;
        playbackController = null;
        playListMetrics = null;
    }

    instance = {
        initialize: initialize,
        getStreamProcessor: getStreamProcessor,
        getSeekTarget: getSeekTarget,
        setSeekTarget: setSeekTarget,
        getFragmentModel: getFragmentModel,
        setTimeToLoadDelay: setTimeToLoadDelay,
        getTimeToLoadDelay: getTimeToLoadDelay,
        replaceCanceledRequests: replaceCanceledRequests,
        start: start,
        stop: stop,
        reset: reset,
        setPlayList: setPlayList,
        finalisePlayList: finalisePlayList
    };

    setup();

    return instance;
}

ScheduleController.__dashjs_factory_name = 'ScheduleController';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(ScheduleController);
module.exports = exports['default'];

},{"../../core/Debug.js":5,"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../../dash/DashAdapter.js":11,"../../dash/DashMetrics.js":13,"../TextSourceBuffer.js":41,"../VirtualBuffer.js":43,"../controllers/SourceBufferController.js":56,"../models/MetricsModel.js":65,"../rules/scheduling/BufferLevelRule.js":80,"../rules/scheduling/NextFragmentRequestRule.js":81,"../utils/LiveEdgeFinder.js":92,"../vo/metrics/PlayList.js":119,"./AbrController.js":46,"./BufferController.js":49,"./PlaybackController.js":54}],56:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _TextSourceBufferJs = _dereq_('../TextSourceBuffer.js');

var _TextSourceBufferJs2 = _interopRequireDefault(_TextSourceBufferJs);

var _MediaControllerJs = _dereq_('./MediaController.js');

var _MediaControllerJs2 = _interopRequireDefault(_MediaControllerJs);

var _dashDashAdapterJs = _dereq_('../../dash/DashAdapter.js');

var _dashDashAdapterJs2 = _interopRequireDefault(_dashDashAdapterJs);

var _utilsErrorHandlerJs = _dereq_('../utils/ErrorHandler.js');

var _utilsErrorHandlerJs2 = _interopRequireDefault(_utilsErrorHandlerJs);

var _StreamControllerJs = _dereq_('./StreamController.js');

var _StreamControllerJs2 = _interopRequireDefault(_StreamControllerJs);

var _TextTracksJs = _dereq_('../TextTracks.js');

var _TextTracksJs2 = _interopRequireDefault(_TextTracksJs);

var _utilsVTTParserJs = _dereq_('../utils/VTTParser.js');

var _utilsVTTParserJs2 = _interopRequireDefault(_utilsVTTParserJs);

var _utilsTTMLParserJs = _dereq_('../utils/TTMLParser.js');

var _utilsTTMLParserJs2 = _interopRequireDefault(_utilsTTMLParserJs);

var _modelsVideoModelJs = _dereq_('../models/VideoModel.js');

var _modelsVideoModelJs2 = _interopRequireDefault(_modelsVideoModelJs);

var _voErrorJs = _dereq_('../vo/Error.js');

var _voErrorJs2 = _interopRequireDefault(_voErrorJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var QUOTA_EXCEEDED_ERROR_CODE = 22;

function SourceBufferController() {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        dashManifestModel = undefined;

    function createSourceBuffer(mediaSource, mediaInfo) {

        var codec = mediaInfo.codec;
        var buffer = null;

        try {
            // Safari claims to support anything starting 'application/mp4'.
            // it definitely doesn't understand 'application/mp4;codecs="stpp"'
            // - currently no browser does, so check for it and use our own
            // implementation. The same is true for codecs="wvtt".
            if (codec.match(/application\/mp4;\s*codecs="(stpp|wvtt)"/i)) {
                throw new _voErrorJs2['default']('not really supported');
            }

            buffer = mediaSource.addSourceBuffer(codec);
        } catch (ex) {
            if (mediaInfo.isText || codec.indexOf('codecs="stpp"') !== -1 || codec.indexOf('codecs="wvtt"') !== -1) {
                buffer = (0, _TextSourceBufferJs2['default'])(context).getInstance();
                buffer.setConfig({
                    errHandler: (0, _utilsErrorHandlerJs2['default'])(context).getInstance(),
                    adapter: (0, _dashDashAdapterJs2['default'])(context).getInstance(),
                    dashManifestModel: dashManifestModel,
                    mediaController: (0, _MediaControllerJs2['default'])(context).getInstance(),
                    videoModel: (0, _modelsVideoModelJs2['default'])(context).getInstance(),
                    streamController: (0, _StreamControllerJs2['default'])(context).getInstance(),
                    textTracks: (0, _TextTracksJs2['default'])(context).getInstance(),
                    VTTParser: (0, _utilsVTTParserJs2['default'])(context).getInstance(),
                    TTMLParser: (0, _utilsTTMLParserJs2['default'])(context).getInstance()

                });
            } else {
                throw ex;
            }
        }

        return buffer;
    }

    function removeSourceBuffer(mediaSource, buffer) {
        try {
            mediaSource.removeSourceBuffer(buffer);
        } catch (ex) {}
    }

    function getBufferRange(buffer, time, tolerance) {
        var ranges = null;
        var start = 0;
        var end = 0;
        var firstStart = null;
        var lastEnd = null;
        var gap = 0;

        var len, i;

        var toler = tolerance || 0.15;

        try {
            ranges = buffer.buffered;
        } catch (ex) {
            return null;
        }

        if (ranges !== null && ranges !== undefined) {
            for (i = 0, len = ranges.length; i < len; i++) {
                start = ranges.start(i);
                end = ranges.end(i);
                if (firstStart === null) {
                    gap = Math.abs(start - time);
                    if (time >= start && time < end) {
                        // start the range
                        firstStart = start;
                        lastEnd = end;
                    } else if (gap <= toler) {
                        // start the range even though the buffer does not contain time 0
                        firstStart = start;
                        lastEnd = end;
                    }
                } else {
                    gap = start - lastEnd;
                    if (gap <= toler) {
                        // the discontinuity is smaller than the tolerance, combine the ranges
                        lastEnd = end;
                    } else {
                        break;
                    }
                }
            }

            if (firstStart !== null) {
                return { start: firstStart, end: lastEnd };
            }
        }

        return null;
    }

    function getAllRanges(buffer) {
        var ranges = null;

        try {
            ranges = buffer.buffered;
            return ranges;
        } catch (ex) {
            return null;
        }
    }

    function getTotalBufferedTime(buffer) {
        var ranges = getAllRanges(buffer);
        var totalBufferedTime = 0;
        var ln, i;

        if (!ranges) return totalBufferedTime;

        for (i = 0, ln = ranges.length; i < ln; i++) {
            totalBufferedTime += ranges.end(i) - ranges.start(i);
        }

        return totalBufferedTime;
    }

    function getBufferLength(buffer, time, tolerance) {

        var range, length;

        range = getBufferRange(buffer, time, tolerance);

        if (range === null) {
            length = 0;
        } else {
            length = range.end - time;
        }

        return length;
    }

    function getRangeDifference(currentRanges, buffer) {
        if (!buffer) return null;

        //TODO we may need to look for a more elegant and robust method
        // The logic below checks that is the difference between currentRanges and actual SourceBuffer ranges

        var newRanges = getAllRanges(buffer);
        var newStart, newEnd, equalStart, equalEnd, currentRange, nextCurrentRange, nextNewRange, hasRange, diff;

        if (!newRanges) return null;

        for (var i = 0, ln = newRanges.length; i < ln; i++) {
            hasRange = currentRanges.length > i;
            currentRange = hasRange ? { start: currentRanges.start(i), end: currentRanges.end(i) } : null;
            newStart = newRanges.start(i);
            newEnd = newRanges.end(i);

            // if there is no range with the same index it means that a new range has been added. This range is
            // the difference we are looking for
            // Example
            // current ranges
            // 0|---range1---|4  8|--range2--|12
            // new ranges
            // 0|---range1---|4| 8|--range2--|12  16|--range3--|20

            if (!currentRange) {
                diff = { start: newStart, end: newEnd };
                return diff;
            }

            equalStart = currentRange.start === newStart;
            equalEnd = currentRange.end === newEnd;

            // if ranges are equal do nothing here and go the next ranges
            if (equalStart && equalEnd) continue;

            // start or/and end of the range has been changed
            if (equalStart) {
                diff = { start: currentRange.end, end: newEnd };
            } else if (equalEnd) {
                diff = { start: newStart, end: currentRange.start };
            } else {
                // new range has been added before the current one
                diff = { start: newStart, end: newEnd };
                return diff;
            }

            // if there is next current range but no next new range (it it is not equal the next current range) it means
            // that the ranges have been merged
            // Example 1
            // current ranges
            // 0|---range1---|4  8|--range2--|12  16|---range3---|
            // new ranges
            // 0|-----------range1-----------|12  16|---range3--|
            nextCurrentRange = currentRanges.length > i + 1 ? { start: currentRanges.start(i + 1), end: currentRanges.end(i + 1) } : null;
            nextNewRange = i + 1 < ln ? { start: newRanges.start(i + 1), end: newRanges.end(i + 1) } : null;

            if (nextCurrentRange && (!nextNewRange || nextNewRange.start !== nextCurrentRange.start || nextNewRange.end !== nextCurrentRange.end)) {
                diff.end = nextCurrentRange.start;
            }

            return diff;
        }

        return null;
    }

    function append(buffer, chunk) {
        var bytes = chunk.bytes;
        var appendMethod = 'append' in buffer ? 'append' : 'appendBuffer' in buffer ? 'appendBuffer' : null;
        // our user-defined sourcebuffer-like object has Object as its
        // prototype whereas built-in SourceBuffers will have something
        // more sensible. do not pass chunk to built-in append.
        var acceptsChunk = Object.prototype.toString.call(buffer).slice(8, -1) === 'Object';

        if (!appendMethod) return;

        try {
            waitForUpdateEnd(buffer, function () {
                if (acceptsChunk) {
                    // chunk.start is used in calculations by TextSourceBuffer
                    buffer[appendMethod](bytes, chunk);
                } else {
                    buffer[appendMethod](bytes);
                }
                // updating is in progress, we should wait for it to complete before signaling that this operation is done
                waitForUpdateEnd(buffer, function () {
                    eventBus.trigger(_coreEventsEventsJs2['default'].SOURCEBUFFER_APPEND_COMPLETED, { buffer: buffer, bytes: bytes });
                });
            });
        } catch (err) {
            eventBus.trigger(_coreEventsEventsJs2['default'].SOURCEBUFFER_APPEND_COMPLETED, { buffer: buffer, bytes: bytes, error: new _voErrorJs2['default'](err.code, err.message, null) });
        }
    }

    function remove(buffer, start, end, mediaSource) {

        try {
            // make sure that the given time range is correct. Otherwise we will get InvalidAccessError
            waitForUpdateEnd(buffer, function () {
                if (start >= 0 && end > start && mediaSource.readyState !== 'ended') {
                    buffer.remove(start, end);
                }
                // updating is in progress, we should wait for it to complete before signaling that this operation is done
                waitForUpdateEnd(buffer, function () {
                    eventBus.trigger(_coreEventsEventsJs2['default'].SOURCEBUFFER_REMOVE_COMPLETED, { buffer: buffer, from: start, to: end });
                });
            });
        } catch (err) {
            eventBus.trigger(_coreEventsEventsJs2['default'].SOURCEBUFFER_REMOVE_COMPLETED, { buffer: buffer, from: start, to: end, error: new _voErrorJs2['default'](err.code, err.message, null) });
        }
    }

    function abort(mediaSource, buffer) {
        try {
            if (mediaSource.readyState === 'open') {
                buffer.abort();
            }
        } catch (ex) {}
    }

    function setConfig(config) {
        if (!config) return;

        if (config.dashManifestModel) {
            dashManifestModel = config.dashManifestModel;
        }
    }

    function waitForUpdateEnd(buffer, callback) {
        var intervalId;
        var CHECK_INTERVAL = 50;

        var checkIsUpdateEnded = function checkIsUpdateEnded() {
            // if updating is still in progress do nothing and wait for the next check again.
            if (buffer.updating) return;
            // updating is completed, now we can stop checking and resolve the promise
            clearInterval(intervalId);
            callback();
        };

        var updateEndHandler = function updateEndHandler() {
            if (buffer.updating) return;

            buffer.removeEventListener('updateend', updateEndHandler, false);
            callback();
        };

        if (!buffer.updating) {
            callback();
            return;
        }

        // use updateend event if possible
        if (typeof buffer.addEventListener === 'function') {
            try {
                buffer.addEventListener('updateend', updateEndHandler, false);
            } catch (err) {
                // use setInterval to periodically check if updating has been completed
                intervalId = setInterval(checkIsUpdateEnded, CHECK_INTERVAL);
            }
        } else {
            // use setInterval to periodically check if updating has been completed
            intervalId = setInterval(checkIsUpdateEnded, CHECK_INTERVAL);
        }
    }

    instance = {
        append: append,
        remove: remove,
        abort: abort,
        createSourceBuffer: createSourceBuffer,
        removeSourceBuffer: removeSourceBuffer,
        getBufferRange: getBufferRange,
        getAllRanges: getAllRanges,
        getTotalBufferedTime: getTotalBufferedTime,
        getBufferLength: getBufferLength,
        getRangeDifference: getRangeDifference,
        setConfig: setConfig
    };

    return instance;
}

SourceBufferController.__dashjs_factory_name = 'SourceBufferController';
var factory = _coreFactoryMakerJs2['default'].getSingletonFactory(SourceBufferController);
factory.QUOTA_EXCEEDED_ERROR_CODE = QUOTA_EXCEEDED_ERROR_CODE;
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../../dash/DashAdapter.js":11,"../TextSourceBuffer.js":41,"../TextTracks.js":42,"../models/VideoModel.js":67,"../utils/ErrorHandler.js":90,"../utils/TTMLParser.js":95,"../utils/VTTParser.js":97,"../vo/Error.js":100,"./MediaController.js":52,"./StreamController.js":57}],57:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _PlaybackControllerJs = _dereq_('./PlaybackController.js');

var _PlaybackControllerJs2 = _interopRequireDefault(_PlaybackControllerJs);

var _StreamJs = _dereq_('../Stream.js');

var _StreamJs2 = _interopRequireDefault(_StreamJs);

var _ManifestUpdaterJs = _dereq_('../ManifestUpdater.js');

var _ManifestUpdaterJs2 = _interopRequireDefault(_ManifestUpdaterJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _modelsURIQueryAndFragmentModelJs = _dereq_('../models/URIQueryAndFragmentModel.js');

var _modelsURIQueryAndFragmentModelJs2 = _interopRequireDefault(_modelsURIQueryAndFragmentModelJs);

var _modelsVideoModelJs = _dereq_('../models/VideoModel.js');

var _modelsVideoModelJs2 = _interopRequireDefault(_modelsVideoModelJs);

var _modelsMediaPlayerModelJs = _dereq_('../models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _voMetricsPlayListJs = _dereq_('../vo/metrics/PlayList.js');

var _voMetricsPlayListJs2 = _interopRequireDefault(_voMetricsPlayListJs);

var _coreDebugJs = _dereq_('../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

function StreamController() {

    var STREAM_END_THRESHOLD = 0.5;

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        capabilities = undefined,
        manifestUpdater = undefined,
        manifestLoader = undefined,
        manifestModel = undefined,
        dashManifestModel = undefined,
        adapter = undefined,
        metricsModel = undefined,
        dashMetrics = undefined,
        liveEdgeFinder = undefined,
        mediaSourceController = undefined,
        timeSyncController = undefined,
        baseURLController = undefined,
        virtualBuffer = undefined,
        errHandler = undefined,
        timelineConverter = undefined,
        streams = undefined,
        activeStream = undefined,
        protectionController = undefined,
        protectionData = undefined,
        autoPlay = undefined,
        isStreamSwitchingInProgress = undefined,
        isUpdating = undefined,
        hasMediaError = undefined,
        hasInitialisationError = undefined,
        mediaSource = undefined,
        videoModel = undefined,
        playbackController = undefined,
        mediaPlayerModel = undefined,
        isPaused = undefined,
        initialPlayback = undefined,
        playListMetrics = undefined;

    function setup() {
        protectionController = null;
        streams = [];
        mediaPlayerModel = (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance();
        autoPlay = true;
        isStreamSwitchingInProgress = false;
        isUpdating = false;
        isPaused = false;
        initialPlayback = true;
        playListMetrics = null;
        hasMediaError = false;
        hasInitialisationError = false;
    }

    function initialize(autoPl, protData) {
        autoPlay = autoPl;
        protectionData = protData;
        timelineConverter.initialize();

        manifestUpdater = (0, _ManifestUpdaterJs2['default'])(context).getInstance();
        manifestUpdater.setConfig({
            log: log,
            manifestModel: manifestModel,
            dashManifestModel: dashManifestModel
        });
        manifestUpdater.initialize(manifestLoader);

        videoModel = (0, _modelsVideoModelJs2['default'])(context).getInstance();
        playbackController = (0, _PlaybackControllerJs2['default'])(context).getInstance();
        playbackController.setConfig({
            streamController: instance,
            timelineConverter: timelineConverter,
            metricsModel: metricsModel,
            dashMetrics: dashMetrics,
            manifestModel: manifestModel,
            dashManifestModel: dashManifestModel,
            adapter: adapter,
            videoModel: videoModel
        });

        eventBus.on(_coreEventsEventsJs2['default'].TIME_SYNCHRONIZATION_COMPLETED, onTimeSyncCompleted, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_TIME_UPDATED, onPlaybackTimeUpdated, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_ENDED, onEnded, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_ERROR, onPlaybackError, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_STARTED, onPlaybackStarted, this);
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_PAUSED, onPlaybackPaused, this);
        eventBus.on(_coreEventsEventsJs2['default'].MANIFEST_UPDATED, onManifestUpdated, this);
        eventBus.on(_coreEventsEventsJs2['default'].STREAM_BUFFERING_COMPLETED, onStreamBufferingCompleted, this);
    }

    function flushPlaylistMetrics(reason, time) {
        time = time || new Date();

        if (playListMetrics) {
            if (activeStream) {
                activeStream.getProcessors().forEach(function (p) {
                    var ctrlr = p.getScheduleController();
                    if (ctrlr) {
                        ctrlr.finalisePlayList(time, reason);
                    }
                });
            }

            metricsModel.addPlayList(playListMetrics);

            playListMetrics = null;
        }
    }

    function addPlaylistMetrics(startReason) {
        playListMetrics = new _voMetricsPlayListJs2['default']();
        playListMetrics.start = new Date();
        playListMetrics.mstart = playbackController.getTime() * 1000;
        playListMetrics.starttype = startReason;

        if (activeStream) {
            activeStream.getProcessors().forEach(function (p) {
                var ctrlr = p.getScheduleController();
                if (ctrlr) {
                    ctrlr.setPlayList(playListMetrics);
                }
            });
        }
    }
    /*
     * StreamController aggregates all streams defined in the manifest file
     * and implements corresponding logic to switch between them.
     */
    function fireSwitchEvent(eventType, fromStream, toStream) {
        eventBus.trigger(eventType, { fromStreamInfo: fromStream ? fromStream.getStreamInfo() : null, toStreamInfo: toStream.getStreamInfo() });
    }

    function startAutoPlay() {
        if (!activeStream.isActivated() || !initialPlayback) return;
        // only first stream must be played automatically during playback initialization
        if (activeStream.getStreamInfo().index === 0) {
            activeStream.startEventController();
            if (autoPlay) {
                playbackController.play();
            }
        }
    }

    function onPlaybackError(e) {
        var code = e.error ? e.error.code : 0;
        var msg = '';

        if (code === -1) {
            // not an error!
            return;
        }

        switch (code) {
            case 1:
                msg = 'MEDIA_ERR_ABORTED';
                break;
            case 2:
                msg = 'MEDIA_ERR_NETWORK';
                break;
            case 3:
                msg = 'MEDIA_ERR_DECODE';
                break;
            case 4:
                msg = 'MEDIA_ERR_SRC_NOT_SUPPORTED';
                break;
            case 5:
                msg = 'MEDIA_ERR_ENCRYPTED';
                break;
            default:
                msg = 'UNKNOWN';
                break;
        }

        hasMediaError = true;

        if (e.error.msExtendedCode) {
            msg += ' (0x' + (e.error.msExtendedCode >>> 0).toString(16).toUpperCase() + ')';
        }

        log('Video Element Error: ' + msg);
        if (e.error) {
            log(e.error);
        }
        errHandler.mediaSourceError(msg);
        reset();
    }

    /*
     * Called when current playback position is changed.
     * Used to determine the time current stream is finished and we should switch to the next stream.
     */
    function onPlaybackTimeUpdated(e) {
        var playbackQuality = videoModel.getPlaybackQuality();
        if (playbackQuality) {
            metricsModel.addDroppedFrames('video', playbackQuality);
        }

        // Sometimes after seeking timeUpdateHandler is called before seekingHandler and a new stream starts
        // from beginning instead of from a chosen position. So we do nothing if the player is in the seeking state
        if (playbackController.isSeeking()) return;

        // check if stream end is reached
        if (e.timeToEnd < STREAM_END_THRESHOLD) {
            mediaSourceController.signalEndOfStream(mediaSource);
        }
    }

    function onEnded() {

        var nextStream = getNextStream();

        if (nextStream) {
            switchStream(activeStream, nextStream, NaN);
        }

        flushPlaylistMetrics(nextStream ? _voMetricsPlayListJs2['default'].Trace.END_OF_PERIOD_STOP_REASON : _voMetricsPlayListJs2['default'].Trace.END_OF_CONTENT_STOP_REASON);
    }

    function onPlaybackSeeking(e) {
        var seekingStream = getStreamForTime(e.seekTime);

        if (seekingStream && seekingStream !== activeStream) {
            flushPlaylistMetrics(_voMetricsPlayListJs2['default'].Trace.END_OF_PERIOD_STOP_REASON);
            switchStream(activeStream, seekingStream, e.seekTime);
        } else {
            flushPlaylistMetrics(_voMetricsPlayListJs2['default'].Trace.USER_REQUEST_STOP_REASON);
        }

        addPlaylistMetrics(_voMetricsPlayListJs2['default'].SEEK_START_REASON);
    }

    function onPlaybackStarted() /*e*/{
        if (initialPlayback) {
            initialPlayback = false;
            addPlaylistMetrics(_voMetricsPlayListJs2['default'].INITIAL_PLAYOUT_START_REASON);
        } else {
            if (isPaused) {
                isPaused = false;
                addPlaylistMetrics(_voMetricsPlayListJs2['default'].RESUME_FROM_PAUSE_START_REASON);
            }
        }
    }

    function onPlaybackPaused(e) {
        if (!e.ended) {
            isPaused = true;
            flushPlaylistMetrics(_voMetricsPlayListJs2['default'].Trace.USER_REQUEST_STOP_REASON);
        }
    }

    /*
     * Handles the current stream buffering end moment to start the next stream buffering
     * Removing MP logic from this for now, we removing the complexity of buffering into next period for now.
     * this handler's logic caused Firefox and Safari to not period switch since the end event did not fire due to this.
     */
    function onStreamBufferingCompleted(e) {
        //var nextStream = getNextStream();
        var isLast = e.streamInfo.isLast;

        if (mediaSource && isLast) {
            mediaSourceController.signalEndOfStream(mediaSource);
        }
        //if (!nextStream) return;
        //nextStream.activate(mediaSource);
    }

    function getNextStream() {
        var start = activeStream.getStreamInfo().start;
        var duration = activeStream.getStreamInfo().duration;

        return streams.filter(function (stream) {
            return stream.getStreamInfo().start === start + duration;
        })[0];
    }

    function getStreamForTime(time) {
        var duration = 0;
        var stream = null;

        var ln = streams.length;

        if (ln > 0) {
            duration += streams[0].getStartTime();
        }

        for (var i = 0; i < ln; i++) {
            stream = streams[i];
            duration += stream.getDuration();

            if (time < duration) {
                return stream;
            }
        }

        return null;
    }

    /**
     * Returns a playhead time, in seconds, converted to be relative
     * to the start of an identified stream/period or null if no such stream
     */
    function getTimeRelativeToStreamId(time, id) {
        var stream = null;
        var baseStart = 0;
        var streamStart = 0;
        var streamDur = null;

        var ln = streams.length;

        for (var i = 0; i < ln; i++) {
            stream = streams[i];
            streamStart = stream.getStartTime();
            streamDur = stream.getDuration();

            // use start time, if not undefined or NaN or similar
            if (Number.isFinite(streamStart)) {
                baseStart = streamStart;
            }

            if (stream.getId() === id) {
                return time - baseStart;
            } else {
                // use duration if not undefined or NaN or similar
                if (Number.isFinite(streamDur)) {
                    baseStart += streamDur;
                }
            }
        }

        return null;
    }

    function getActiveStreamCommonEarliestTime() {
        var commonEarliestTime = [];
        activeStream.getProcessors().forEach(function (p) {
            commonEarliestTime.push(p.getIndexHandler().getEarliestTime());
        });
        return Math.min.apply(Math, commonEarliestTime);
    }

    function switchStream(from, to, seekTime) {

        if (isStreamSwitchingInProgress || !from || !to || from === to) return;

        isStreamSwitchingInProgress = true;
        fireSwitchEvent(_coreEventsEventsJs2['default'].PERIOD_SWITCH_STARTED, from, to);

        function onMediaSourceReady() {
            if (!isNaN(seekTime)) {
                playbackController.seek(seekTime); //we only need to call seek here, IndexHandlerTime was set from seeking event
            } else {
                    (function () {
                        var startTime = playbackController.getStreamStartTime(true);
                        activeStream.getProcessors().forEach(function (p) {
                            adapter.setIndexHandlerTime(p, startTime);
                        });
                        playbackController.seek(startTime); //seek to period start time
                    })();
                }
            playbackController.play();
            activeStream.startEventController();
            isStreamSwitchingInProgress = false;
            fireSwitchEvent(_coreEventsEventsJs2['default'].PERIOD_SWITCH_COMPLETED, from, to);
        }

        from.deactivate();
        activeStream = to;
        playbackController.initialize(activeStream.getStreamInfo());
        setupMediaSource(onMediaSourceReady);
    }

    function setupMediaSource(callback) {

        var sourceUrl = undefined;

        function onMediaSourceOpen() {
            log('MediaSource is open!');

            window.URL.revokeObjectURL(sourceUrl);
            mediaSource.removeEventListener('sourceopen', onMediaSourceOpen);
            mediaSource.removeEventListener('webkitsourceopen', onMediaSourceOpen);
            setMediaDuration();
            activeStream.activate(mediaSource);

            if (callback) {
                callback();
            }
        }

        if (!mediaSource) {
            mediaSource = mediaSourceController.createMediaSource();
        } else {
            mediaSourceController.detachMediaSource(videoModel);
        }

        mediaSource.addEventListener('sourceopen', onMediaSourceOpen, false);
        mediaSource.addEventListener('webkitsourceopen', onMediaSourceOpen, false);
        sourceUrl = mediaSourceController.attachMediaSource(mediaSource, videoModel);
        log('MediaSource attached to element.  Waiting on open...');
    }

    function setMediaDuration() {
        var manifestDuration, mediaDuration;

        manifestDuration = activeStream.getStreamInfo().manifestInfo.duration;
        mediaDuration = mediaSourceController.setDuration(mediaSource, manifestDuration);
        log('Duration successfully set to: ' + mediaDuration);
    }

    function composeStreams() {
        var manifest = manifestModel.getValue();
        var metrics = metricsModel.getMetricsFor('stream');
        var manifestUpdateInfo = dashMetrics.getCurrentManifestUpdate(metrics);
        var remainingStreams = [];
        var streamInfo, pLen, sLen, pIdx, sIdx, streamsInfo, stream;

        if (!manifest) return;

        streamsInfo = adapter.getStreamsInfo(manifest);
        if (protectionController) {
            eventBus.trigger(_coreEventsEventsJs2['default'].PROTECTION_CREATED, { controller: protectionController, manifest: manifest });
            protectionController.setMediaElement(videoModel.getElement());
            if (protectionData) {
                protectionController.setProtectionData(protectionData);
            }
        }

        try {
            if (streamsInfo.length === 0) {
                throw new Error('There are no streams');
            }

            metricsModel.updateManifestUpdateInfo(manifestUpdateInfo, {
                currentTime: playbackController.getTime(),
                buffered: videoModel.getElement().buffered,
                presentationStartTime: streamsInfo[0].start,
                clientTimeOffset: timelineConverter.getClientTimeOffset()
            });

            isUpdating = true;

            for (pIdx = 0, pLen = streamsInfo.length; pIdx < pLen; pIdx++) {
                streamInfo = streamsInfo[pIdx];
                for (sIdx = 0, sLen = streams.length; sIdx < sLen; sIdx++) {
                    // If the stream already exists we just need to update the values we got from the updated manifest
                    if (streams[sIdx].getId() === streamInfo.id) {
                        stream = streams[sIdx];
                        remainingStreams.push(stream);
                        stream.updateData(streamInfo);
                    }
                }
                // If the Stream object does not exist we probably loaded the manifest the first time or it was
                // introduced in the updated manifest, so we need to create a new Stream and perform all the initialization operations
                if (!stream) {

                    stream = (0, _StreamJs2['default'])(context).create({
                        manifestModel: manifestModel,
                        manifestUpdater: manifestUpdater,
                        adapter: adapter,
                        timelineConverter: timelineConverter,
                        capabilities: capabilities,
                        errHandler: errHandler,
                        baseURLController: baseURLController
                    });
                    stream.initialize(streamInfo, protectionController);

                    eventBus.on(_coreEventsEventsJs2['default'].STREAM_INITIALIZED, onStreamInitialized, this);
                    remainingStreams.push(stream);

                    if (activeStream) {
                        stream.updateData(streamInfo);
                    }
                }
                metricsModel.addManifestUpdateStreamInfo(manifestUpdateInfo, streamInfo.id, streamInfo.index, streamInfo.start, streamInfo.duration);
                stream = null;
            }

            streams = remainingStreams;

            // If the active stream has not been set up yet, let it be the first Stream in the list
            if (!activeStream) {
                activeStream = streams[0];
                fireSwitchEvent(_coreEventsEventsJs2['default'].PERIOD_SWITCH_STARTED, null, activeStream);
                playbackController.initialize(activeStream.getStreamInfo());
                fireSwitchEvent(_coreEventsEventsJs2['default'].PERIOD_SWITCH_COMPLETED, null, activeStream);
            }

            if (!mediaSource) {
                setupMediaSource();
            }

            isUpdating = false;
            checkIfUpdateCompleted();
        } catch (e) {
            errHandler.manifestError(e.message, 'nostreamscomposed', manifest);
            hasInitialisationError = true;
            reset();
        }
    }

    function checkIfUpdateCompleted() {
        if (isUpdating) return;

        var ln = streams.length;
        var i = 0;

        startAutoPlay();

        for (i; i < ln; i++) {
            if (!streams[i].isInitialized()) return;
        }

        eventBus.trigger(_coreEventsEventsJs2['default'].STREAMS_COMPOSED);
    }

    function onStreamInitialized() /*e*/{
        checkIfUpdateCompleted();
    }

    function onTimeSyncCompleted() /*e*/{
        composeStreams();
    }

    function onManifestUpdated(e) {
        if (!e.error) {
            //Since streams are not composed yet , need to manually look up useCalculatedLiveEdgeTime to detect if stream
            //is SegmentTimeline to avoid using time source
            var manifest = e.manifest;
            var streamInfo = adapter.getStreamsInfo(manifest)[0];
            var mediaInfo = adapter.getMediaInfoForType(manifest, streamInfo, 'video') || adapter.getMediaInfoForType(manifest, streamInfo, 'audio');

            var adaptation, useCalculatedLiveEdgeTime;

            if (mediaInfo) {
                adaptation = adapter.getDataForMedia(mediaInfo);
                useCalculatedLiveEdgeTime = dashManifestModel.getRepresentationsForAdaptation(manifest, adaptation)[0].useCalculatedLiveEdgeTime;

                if (useCalculatedLiveEdgeTime) {
                    log('SegmentTimeline detected using calculated Live Edge Time');
                    mediaPlayerModel.setUseManifestDateHeaderTimeSource(false);
                }
            }

            var manifestUTCTimingSources = dashManifestModel.getUTCTimingSources(e.manifest);
            var allUTCTimingSources = !dashManifestModel.getIsDynamic(manifest) || useCalculatedLiveEdgeTime ? manifestUTCTimingSources : manifestUTCTimingSources.concat(mediaPlayerModel.getUTCTimingSources());
            var isHTTPS = (0, _modelsURIQueryAndFragmentModelJs2['default'])(context).getInstance().isManifestHTTPS();

            //If https is detected on manifest then lets apply that protocol to only the default time source(s). In the future we may find the need to apply this to more then just default so left code at this level instead of in MediaPlayer.
            allUTCTimingSources.forEach(function (item) {
                if (item.value.replace(/.*?:\/\//g, '') === _modelsMediaPlayerModelJs2['default'].DEFAULT_UTC_TIMING_SOURCE.value.replace(/.*?:\/\//g, '')) {
                    item.value = item.value.replace(isHTTPS ? new RegExp(/^(http:)?\/\//i) : new RegExp(/^(https:)?\/\//i), isHTTPS ? 'https://' : 'http://');
                    log('Matching default timing source protocol to manifest protocol: ', item.value);
                }
            });

            baseURLController.initialize(manifest);

            timeSyncController.setConfig({
                metricsModel: metricsModel,
                dashMetrics: dashMetrics
            });
            timeSyncController.initialize(allUTCTimingSources, mediaPlayerModel.getUseManifestDateHeaderTimeSource());
        } else {
            hasInitialisationError = true;
            reset();
        }
    }

    function getAutoPlay() {
        return autoPlay;
    }

    function getActiveStreamInfo() {
        return activeStream ? activeStream.getStreamInfo() : null;
    }

    function isStreamActive(streamInfo) {
        return activeStream.getId() === streamInfo.id;
    }

    function getStreamById(id) {
        return streams.filter(function (item) {
            return item.getId() === id;
        })[0];
    }

    function load(url) {
        manifestLoader.load(url);
    }

    function loadWithManifest(manifest) {
        manifestUpdater.setManifest(manifest);
    }

    function setConfig(config) {
        if (!config) return;

        if (config.capabilities) {
            capabilities = config.capabilities;
        }
        if (config.manifestLoader) {
            manifestLoader = config.manifestLoader;
        }
        if (config.manifestModel) {
            manifestModel = config.manifestModel;
        }
        if (config.dashManifestModel) {
            dashManifestModel = config.dashManifestModel;
        }
        if (config.protectionController) {
            protectionController = config.protectionController;
        }
        if (config.adapter) {
            adapter = config.adapter;
        }
        if (config.metricsModel) {
            metricsModel = config.metricsModel;
        }
        if (config.dashMetrics) {
            dashMetrics = config.dashMetrics;
        }
        if (config.liveEdgeFinder) {
            liveEdgeFinder = config.liveEdgeFinder;
        }
        if (config.mediaSourceController) {
            mediaSourceController = config.mediaSourceController;
        }
        if (config.timeSyncController) {
            timeSyncController = config.timeSyncController;
        }
        if (config.baseURLController) {
            baseURLController = config.baseURLController;
        }
        if (config.virtualBuffer) {
            virtualBuffer = config.virtualBuffer;
        }
        if (config.errHandler) {
            errHandler = config.errHandler;
        }
        if (config.timelineConverter) {
            timelineConverter = config.timelineConverter;
        }
    }

    function reset() {
        timeSyncController.reset();

        flushPlaylistMetrics(hasMediaError || hasInitialisationError ? _voMetricsPlayListJs2['default'].Trace.FAILURE_STOP_REASON : _voMetricsPlayListJs2['default'].Trace.USER_REQUEST_STOP_REASON);

        for (var i = 0, ln = streams.length; i < ln; i++) {
            var stream = streams[i];
            eventBus.off(_coreEventsEventsJs2['default'].STREAM_INITIALIZED, onStreamInitialized, this);
            stream.reset(hasMediaError);
        }

        streams = [];

        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_TIME_UPDATED, onPlaybackTimeUpdated, this);
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, this);
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_ERROR, onPlaybackError, this);
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_STARTED, onPlaybackStarted, this);
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_PAUSED, onPlaybackPaused, this);
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_ENDED, onEnded, this);
        eventBus.off(_coreEventsEventsJs2['default'].MANIFEST_UPDATED, onManifestUpdated, this);
        eventBus.off(_coreEventsEventsJs2['default'].STREAM_BUFFERING_COMPLETED, onStreamBufferingCompleted, this);

        baseURLController.reset();
        manifestUpdater.reset();
        metricsModel.clearAllCurrentMetrics();
        manifestModel.setValue(null);
        manifestLoader.reset();
        timelineConverter.reset();
        liveEdgeFinder.reset();
        adapter.reset();
        virtualBuffer.reset();
        isStreamSwitchingInProgress = false;
        isUpdating = false;
        activeStream = null;
        hasMediaError = false;
        hasInitialisationError = false;
        initialPlayback = true;
        isPaused = false;

        if (mediaSource) {
            mediaSourceController.detachMediaSource(videoModel);
            mediaSource = null;
        }
        videoModel = null;
        if (protectionController) {
            protectionController.setMediaElement(null);
            protectionController = null;
            protectionData = null;
            if (manifestModel.getValue()) {
                eventBus.trigger(_coreEventsEventsJs2['default'].PROTECTION_DESTROYED, { data: manifestModel.getValue().url });
            }
        }

        eventBus.trigger(_coreEventsEventsJs2['default'].STREAM_TEARDOWN_COMPLETE);
    }

    instance = {
        initialize: initialize,
        getAutoPlay: getAutoPlay,
        getActiveStreamInfo: getActiveStreamInfo,
        isStreamActive: isStreamActive,
        getStreamById: getStreamById,
        getTimeRelativeToStreamId: getTimeRelativeToStreamId,
        load: load,
        loadWithManifest: loadWithManifest,
        getActiveStreamCommonEarliestTime: getActiveStreamCommonEarliestTime,
        setConfig: setConfig,
        reset: reset
    };

    setup();

    return instance;
}

StreamController.__dashjs_factory_name = 'StreamController';

exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(StreamController);
module.exports = exports['default'];

},{"../../core/Debug.js":5,"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../ManifestUpdater.js":36,"../Stream.js":39,"../models/MediaPlayerModel.js":64,"../models/URIQueryAndFragmentModel.js":66,"../models/VideoModel.js":67,"../vo/metrics/PlayList.js":119,"./PlaybackController.js":54}],58:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function TextController(config) {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var sourceBufferController = config.sourceBufferController;
    var errHandler = config.errHandler;

    var instance = undefined,
        initialized = undefined,
        mediaSource = undefined,
        buffer = undefined,
        type = undefined,
        streamProcessor = undefined,
        representationController = undefined;

    function setup() {

        initialized = false;
        mediaSource = null;
        buffer = null;
        type = null;
        streamProcessor = null;
        representationController = null;

        eventBus.on(_coreEventsEventsJs2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this);
        eventBus.on(_coreEventsEventsJs2['default'].INIT_FRAGMENT_LOADED, onInitFragmentLoaded, this);
    }

    function initialize(Type, source, StreamProcessor) {
        type = Type;
        setMediaSource(source);
        streamProcessor = StreamProcessor;
        representationController = streamProcessor.getRepresentationController();
    }

    /**
     * @param mediaInfo object
     * @returns SourceBuffer object
     * @memberof BufferController#
     */
    function createBuffer(mediaInfo) {
        try {
            buffer = sourceBufferController.createSourceBuffer(mediaSource, mediaInfo);

            if (!initialized) {
                if (buffer.hasOwnProperty('initialize')) {
                    buffer.initialize(type, this);
                }
                initialized = true;
            }
        } catch (e) {
            errHandler.mediaSourceError('Error creating ' + type + ' source buffer.');
        }

        return buffer;
    }

    function getBuffer() {
        return buffer;
    }

    function setBuffer(value) {
        buffer = value;
    }

    function setMediaSource(value) {
        mediaSource = value;
    }

    function getStreamProcessor() {
        return streamProcessor;
    }

    function reset(errored) {

        eventBus.off(_coreEventsEventsJs2['default'].DATA_UPDATE_COMPLETED, onDataUpdateCompleted, this);
        eventBus.off(_coreEventsEventsJs2['default'].INIT_FRAGMENT_LOADED, onInitFragmentLoaded, this);

        if (!errored) {
            sourceBufferController.abort(mediaSource, buffer);
            sourceBufferController.removeSourceBuffer(mediaSource, buffer);
        }
    }

    function onDataUpdateCompleted(e) {
        if (e.sender.getStreamProcessor() !== streamProcessor) return;
        eventBus.trigger(_coreEventsEventsJs2['default'].TIMED_TEXT_REQUESTED, { index: 0, sender: e.sender }); //TODO make index dynamic if referring to MP?
    }

    function onInitFragmentLoaded(e) {
        if (e.fragmentModel !== streamProcessor.getFragmentModel() || !e.chunk.bytes) return;
        sourceBufferController.append(buffer, e.chunk);
    }

    instance = {
        initialize: initialize,
        createBuffer: createBuffer,
        getBuffer: getBuffer,
        setBuffer: setBuffer,
        getStreamProcessor: getStreamProcessor,
        setMediaSource: setMediaSource,
        reset: reset
    };

    setup();

    return instance;
}

TextController.__dashjs_factory_name = 'TextController';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(TextController);
module.exports = exports['default'];

},{"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9}],59:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _voErrorJs = _dereq_('./../vo/Error.js');

var _voErrorJs2 = _interopRequireDefault(_voErrorJs);

var _coreEventBusJs = _dereq_('./../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('./../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var TIME_SYNC_FAILED_ERROR_CODE = 1;
var HTTP_TIMEOUT_MS = 5000;

function TimeSyncController() {

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        offsetToDeviceTimeMs = undefined,
        isSynchronizing = undefined,
        isInitialised = undefined,
        useManifestDateHeaderTimeSource = undefined,
        handlers = undefined,
        metricsModel = undefined,
        dashMetrics = undefined;

    function initialize(timingSources, useManifestDateHeader) {
        useManifestDateHeaderTimeSource = useManifestDateHeader;
        offsetToDeviceTimeMs = 0;
        isSynchronizing = false;
        isInitialised = false;

        // a list of known schemeIdUris and a method to call with @value
        handlers = {
            'urn:mpeg:dash:utc:http-head:2014': httpHeadHandler,
            'urn:mpeg:dash:utc:http-xsdate:2014': httpHandler.bind(null, xsdatetimeDecoder),
            'urn:mpeg:dash:utc:http-iso:2014': httpHandler.bind(null, iso8601Decoder),
            'urn:mpeg:dash:utc:direct:2014': directHandler,

            // some specs referencing early ISO23009-1 drafts incorrectly use
            // 2012 in the URI, rather than 2014. support these for now.
            'urn:mpeg:dash:utc:http-head:2012': httpHeadHandler,
            'urn:mpeg:dash:utc:http-xsdate:2012': httpHandler.bind(null, xsdatetimeDecoder),
            'urn:mpeg:dash:utc:http-iso:2012': httpHandler.bind(null, iso8601Decoder),
            'urn:mpeg:dash:utc:direct:2012': directHandler,

            // it isn't clear how the data returned would be formatted, and
            // no public examples available so http-ntp not supported for now.
            // presumably you would do an arraybuffer type xhr and decode the
            // binary data returned but I would want to see a sample first.
            'urn:mpeg:dash:utc:http-ntp:2014': notSupportedHandler,

            // not clear how this would be supported in javascript (in browser)
            'urn:mpeg:dash:utc:ntp:2014': notSupportedHandler,
            'urn:mpeg:dash:utc:sntp:2014': notSupportedHandler
        };

        if (!getIsSynchronizing()) {
            attemptSync(timingSources);
            setIsInitialised(true);
        }
    }

    function setConfig(config) {
        if (!config) return;

        if (config.metricsModel) {
            metricsModel = config.metricsModel;
        }

        if (config.dashMetrics) {
            dashMetrics = config.dashMetrics;
        }
    }

    function getOffsetToDeviceTimeMs() {
        return getOffsetMs();
    }

    function setIsSynchronizing(value) {
        isSynchronizing = value;
    }

    function getIsSynchronizing() {
        return isSynchronizing;
    }

    function setIsInitialised(value) {
        isInitialised = value;
    }

    function setOffsetMs(value) {
        offsetToDeviceTimeMs = value;
    }

    function getOffsetMs() {
        return offsetToDeviceTimeMs;
    }

    // takes xsdatetime and returns milliseconds since UNIX epoch
    // may not be necessary as xsdatetime is very similar to ISO 8601
    // which is natively understood by javascript Date parser
    function alternateXsdatetimeDecoder(xsdatetimeStr) {
        // taken from DashParser - should probably refactor both uses
        var SECONDS_IN_MIN = 60;
        var MINUTES_IN_HOUR = 60;
        var MILLISECONDS_IN_SECONDS = 1000;
        var datetimeRegex = /^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2})(?::([0-9]*)(\.[0-9]*)?)?(?:([+\-])([0-9]{2})([0-9]{2}))?/;

        var utcDate, timezoneOffset;

        var match = datetimeRegex.exec(xsdatetimeStr);

        // If the string does not contain a timezone offset different browsers can interpret it either
        // as UTC or as a local time so we have to parse the string manually to normalize the given date value for
        // all browsers
        utcDate = Date.UTC(parseInt(match[1], 10), parseInt(match[2], 10) - 1, // months start from zero
        parseInt(match[3], 10), parseInt(match[4], 10), parseInt(match[5], 10), match[6] && (parseInt(match[6], 10) || 0), match[7] && parseFloat(match[7]) * MILLISECONDS_IN_SECONDS || 0);
        // If the date has timezone offset take it into account as well
        if (match[9] && match[10]) {
            timezoneOffset = parseInt(match[9], 10) * MINUTES_IN_HOUR + parseInt(match[10], 10);
            utcDate += (match[8] === '+' ? -1 : +1) * timezoneOffset * SECONDS_IN_MIN * MILLISECONDS_IN_SECONDS;
        }

        return new Date(utcDate).getTime();
    }

    // try to use the built in parser, since xsdate is a constrained ISO8601
    // which is supported natively by Date.parse. if that fails, try a
    // regex-based version used elsewhere in this application.
    function xsdatetimeDecoder(xsdatetimeStr) {
        var parsedDate = Date.parse(xsdatetimeStr);

        if (isNaN(parsedDate)) {
            parsedDate = alternateXsdatetimeDecoder(xsdatetimeStr);
        }

        return parsedDate;
    }

    // takes ISO 8601 timestamp and returns milliseconds since UNIX epoch
    function iso8601Decoder(isoStr) {
        return Date.parse(isoStr);
    }

    // takes RFC 1123 timestamp (which is same as ISO8601) and returns
    // milliseconds since UNIX epoch
    function rfc1123Decoder(dateStr) {
        return Date.parse(dateStr);
    }

    function notSupportedHandler(url, onSuccessCB, onFailureCB) {
        onFailureCB();
    }

    function directHandler(xsdatetimeStr, onSuccessCB, onFailureCB) {
        var time = xsdatetimeDecoder(xsdatetimeStr);

        if (!isNaN(time)) {
            onSuccessCB(time);
            return;
        }

        onFailureCB();
    }

    function httpHandler(decoder, url, onSuccessCB, onFailureCB, isHeadRequest) {
        var oncomplete, onload;
        var complete = false;
        var req = new XMLHttpRequest();

        var verb = isHeadRequest ? 'HEAD' : 'GET';
        var urls = url.match(/\S+/g);

        // according to ISO 23009-1, url could be a white-space
        // separated list of URLs. just handle one at a time.
        url = urls.shift();

        oncomplete = function () {
            if (complete) {
                return;
            }

            // we only want to pass through here once per xhr,
            // regardless of whether the load was successful.
            complete = true;

            // if there are more urls to try, call self.
            if (urls.length) {
                httpHandler(decoder, urls.join(' '), onSuccessCB, onFailureCB, isHeadRequest);
            } else {
                onFailureCB();
            }
        };

        onload = function () {
            var time, result;

            if (req.status === 200) {
                time = isHeadRequest ? req.getResponseHeader('Date') : req.response;

                result = decoder(time);

                // decoder returns NaN if non-standard input
                if (!isNaN(result)) {
                    onSuccessCB(result);
                    complete = true;
                }
            }
        };

        req.open(verb, url);
        req.timeout = HTTP_TIMEOUT_MS || 0;
        req.onload = onload;
        req.onloadend = oncomplete;
        req.send();
    }

    function httpHeadHandler(url, onSuccessCB, onFailureCB) {
        httpHandler(rfc1123Decoder, url, onSuccessCB, onFailureCB, true);
    }

    function checkForDateHeader() {
        var metrics = metricsModel.getReadOnlyMetricsFor('stream');
        var dateHeaderValue = dashMetrics.getLatestMPDRequestHeaderValueByID(metrics, 'Date');
        var dateHeaderTime = dateHeaderValue !== null ? new Date(dateHeaderValue).getTime() : Number.NaN;

        if (!isNaN(dateHeaderTime)) {
            setOffsetMs(dateHeaderTime - new Date().getTime());
            completeTimeSyncSequence(false, dateHeaderTime / 1000, offsetToDeviceTimeMs);
        } else {
            completeTimeSyncSequence(true);
        }
    }

    function completeTimeSyncSequence(failed, time, offset) {
        setIsSynchronizing(false);
        eventBus.trigger(_coreEventsEventsJs2['default'].TIME_SYNCHRONIZATION_COMPLETED, { time: time, offset: offset, error: failed ? new _voErrorJs2['default'](TIME_SYNC_FAILED_ERROR_CODE) : null });
    }

    function attemptSync(sources, sourceIndex) {

        // if called with no sourceIndex, use zero (highest priority)
        var index = sourceIndex || 0;

        // the sources should be ordered in priority from the manifest.
        // try each in turn, from the top, until either something
        // sensible happens, or we run out of sources to try.
        var source = sources[index];

        // callback to emit event to listeners
        var onComplete = function onComplete(time, offset) {
            var failed = !time || !offset;
            if (failed && useManifestDateHeaderTimeSource) {
                //Before falling back to binary search , check if date header exists on MPD. if so, use for a time source.
                checkForDateHeader();
            } else {
                completeTimeSyncSequence(failed, time, offset);
            }
        };

        setIsSynchronizing(true);

        if (source) {
            // check if there is a handler for this @schemeIdUri
            if (handlers.hasOwnProperty(source.schemeIdUri)) {
                // if so, call it with its @value
                handlers[source.schemeIdUri](source.value, function (serverTime) {
                    // the timing source returned something useful
                    var deviceTime = new Date().getTime();
                    var offset = serverTime - deviceTime;

                    setOffsetMs(offset);

                    log('Local time:      ' + new Date(deviceTime));
                    log('Server time:     ' + new Date(serverTime));
                    log('Difference (ms): ' + offset);

                    onComplete(serverTime, offset);
                }, function () {
                    // the timing source was probably uncontactable
                    // or returned something we can't use - try again
                    // with the remaining sources
                    attemptSync(sources, index + 1);
                });
            } else {
                // an unknown schemeIdUri must have been found
                // try again with the remaining sources
                attemptSync(sources, index + 1);
            }
        } else {
            // no valid time source could be found, just use device time
            setOffsetMs(0);
            onComplete();
        }
    }

    function reset() {
        setIsInitialised(false);
        setIsSynchronizing(false);
    }

    instance = {
        initialize: initialize,
        getOffsetToDeviceTimeMs: getOffsetToDeviceTimeMs,
        setConfig: setConfig,
        reset: reset
    };

    return instance;
}

TimeSyncController.__dashjs_factory_name = 'TimeSyncController';
var factory = _coreFactoryMakerJs2['default'].getSingletonFactory(TimeSyncController);
factory.TIME_SYNC_FAILED_ERROR_CODE = TIME_SYNC_FAILED_ERROR_CODE;
factory.HTTP_TIMEOUT_MS = HTTP_TIMEOUT_MS;
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/Debug.js":5,"../../core/FactoryMaker.js":7,"./../../core/EventBus.js":6,"./../../core/events/Events.js":9,"./../vo/Error.js":100}],60:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _XlinkLoaderJs = _dereq_('../XlinkLoader.js');

var _XlinkLoaderJs2 = _interopRequireDefault(_XlinkLoaderJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _externalsXml2jsonJs = _dereq_('../../../externals/xml2json.js');

var _externalsXml2jsonJs2 = _interopRequireDefault(_externalsXml2jsonJs);

var RESOLVE_TYPE_ONLOAD = 'onLoad';
var RESOLVE_TYPE_ONACTUATE = 'onActuate';
var ELEMENT_TYPE_PERIOD = 'Period';
var ELEMENT_TYPE_ADAPTATIONSET = 'AdaptationSet';
var ELEMENT_TYPE_EVENTSTREAM = 'EventStream';
var RESOLVE_TO_ZERO = 'urn:mpeg:dash:resolve-to-zero:2013';

function XlinkController(config) {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        matchers = undefined,
        iron = undefined,
        manifest = undefined,
        converter = undefined,
        xlinkLoader = undefined;

    function setup() {
        eventBus.on(_coreEventsEventsJs2['default'].XLINK_ELEMENT_LOADED, onXlinkElementLoaded, instance);

        xlinkLoader = (0, _XlinkLoaderJs2['default'])(context).create({
            errHandler: config.errHandler,
            metricsModel: config.metricsModel,
            requestModifier: config.requestModifier
        });
    }

    function setMatchers(value) {
        matchers = value;
    }

    function setIron(value) {
        iron = value;
    }

    /**
     * <p>Triggers the resolution of the xlink.onLoad attributes in the manifest file </p>
     * @param manifest
     */
    function resolveManifestOnLoad(mpd) {
        var elements;
        // First resolve all periods, so unnecessary requests inside onLoad Periods with Default content are avoided
        converter = new _externalsXml2jsonJs2['default'](matchers, '', true);
        manifest = mpd;
        elements = getElementsToResolve(manifest.Period_asArray, manifest, ELEMENT_TYPE_PERIOD, RESOLVE_TYPE_ONLOAD);
        resolve(elements, ELEMENT_TYPE_PERIOD, RESOLVE_TYPE_ONLOAD);
    }

    function reset() {
        eventBus.off(_coreEventsEventsJs2['default'].XLINK_ELEMENT_LOADED, onXlinkElementLoaded, instance);

        if (xlinkLoader) {
            xlinkLoader.reset();
            xlinkLoader = null;
        }
    }

    function resolve(elements, type, resolveType) {
        var resolveObject = {};
        var element, url, i;

        resolveObject.elements = elements;
        resolveObject.type = type;
        resolveObject.resolveType = resolveType;
        // If nothing to resolve, directly call allElementsLoaded
        if (resolveObject.elements.length === 0) {
            onXlinkAllElementsLoaded(resolveObject);
        }
        for (i = 0; i < resolveObject.elements.length; i++) {
            element = resolveObject.elements[i];
            if (element.url.indexOf('http://') !== -1) {
                url = element.url;
            } else {
                url = element.originalContent.BaseURL + element.url;
            }
            xlinkLoader.load(url, element, resolveObject);
        }
    }

    function onXlinkElementLoaded(event) {
        var element, resolveObject, index;

        var openingTag = '<response>';
        var closingTag = '</response>';
        var mergedContent = '';

        element = event.element;
        resolveObject = event.resolveObject;
        // if the element resolved into content parse the content
        if (element.resolvedContent) {
            // we add a parent elements so the converter is able to parse multiple elements of the same type which are not wrapped inside a container
            index = element.resolvedContent.indexOf('>') + 1; //find the closing position of the xml tag
            mergedContent = element.resolvedContent.substr(0, index) + openingTag + element.resolvedContent.substr(index) + closingTag;
            element.resolvedContent = converter.xml_str2json(mergedContent);
        }
        if (isResolvingFinished(resolveObject)) {
            onXlinkAllElementsLoaded(resolveObject);
        }
    }

    // We got to wait till all elements of the current queue are resolved before merging back
    function onXlinkAllElementsLoaded(resolveObject) {
        var elements = [];
        var i, obj;

        mergeElementsBack(resolveObject);
        if (resolveObject.resolveType === RESOLVE_TYPE_ONACTUATE) {
            eventBus.trigger(_coreEventsEventsJs2['default'].XLINK_READY, { manifest: manifest });
        }
        if (resolveObject.resolveType === RESOLVE_TYPE_ONLOAD) {
            switch (resolveObject.type) {
                // Start resolving the other elements. We can do Adaptation Set and EventStream in parallel
                case ELEMENT_TYPE_PERIOD:
                    for (i = 0; i < manifest[ELEMENT_TYPE_PERIOD + '_asArray'].length; i++) {
                        obj = manifest[ELEMENT_TYPE_PERIOD + '_asArray'][i];
                        if (obj.hasOwnProperty(ELEMENT_TYPE_ADAPTATIONSET + '_asArray')) {
                            elements = elements.concat(getElementsToResolve(obj[ELEMENT_TYPE_ADAPTATIONSET + '_asArray'], obj, ELEMENT_TYPE_ADAPTATIONSET, RESOLVE_TYPE_ONLOAD));
                        }
                        if (obj.hasOwnProperty(ELEMENT_TYPE_EVENTSTREAM + '_asArray')) {
                            elements = elements.concat(getElementsToResolve(obj[ELEMENT_TYPE_EVENTSTREAM + '_asArray'], obj, ELEMENT_TYPE_EVENTSTREAM, RESOLVE_TYPE_ONLOAD));
                        }
                    }
                    resolve(elements, ELEMENT_TYPE_ADAPTATIONSET, RESOLVE_TYPE_ONLOAD);
                    break;
                case ELEMENT_TYPE_ADAPTATIONSET:
                    // TODO: Resolve SegmentList here
                    eventBus.trigger(_coreEventsEventsJs2['default'].XLINK_READY, { manifest: manifest });
                    break;
            }
        }
    }

    // Returns the elements with the specific resolve Type
    function getElementsToResolve(elements, parentElement, type, resolveType) {
        var toResolve = [];
        var element, i, xlinkObject;
        // first remove all the resolve-to-zero elements
        for (i = elements.length - 1; i >= 0; i--) {
            element = elements[i];
            if (element.hasOwnProperty('xlink:href') && element['xlink:href'] === RESOLVE_TO_ZERO) {
                elements.splice(i, 1);
            }
        }
        // now get the elements with the right resolve type
        for (i = 0; i < elements.length; i++) {
            element = elements[i];
            if (element.hasOwnProperty('xlink:href') && element.hasOwnProperty('xlink:actuate') && element['xlink:actuate'] === resolveType) {
                xlinkObject = createXlinkObject(element['xlink:href'], parentElement, type, i, resolveType, element);
                toResolve.push(xlinkObject);
            }
        }
        return toResolve;
    }

    function mergeElementsBack(resolveObject) {
        var resolvedElements = [];
        var element, type, obj, i, j, k;
        // Start merging back from the end because of index shifting. Note that the elements with the same parent have to be ordered by index ascending
        for (i = resolveObject.elements.length - 1; i >= 0; i--) {
            element = resolveObject.elements[i];
            type = element.type + '_asArray';

            // Element couldn't be resolved or is TODO Inappropriate target: Remove all Xlink attributes
            if (!element.resolvedContent || isInappropriateTarget()) {
                delete element.originalContent['xlink:actuate'];
                delete element.originalContent['xlink:href'];
                resolvedElements.push(element.originalContent);
            }
            // Element was successfully resolved
            else if (element.resolvedContent) {
                    for (j = 0; j < element.resolvedContent[type].length; j++) {
                        //TODO Contains another Xlink attribute with xlink:actuate set to onload. Remove all xLink attributes
                        obj = element.resolvedContent[type][j];
                        resolvedElements.push(obj);
                    }
                }
            // Replace the old elements in the parent with the resolved ones
            element.parentElement[type].splice(element.index, 1);
            for (k = 0; k < resolvedElements.length; k++) {
                element.parentElement[type].splice(element.index + k, 0, resolvedElements[k]);
            }
            resolvedElements = [];
        }
        if (resolveObject.elements.length > 0) {
            iron.run(manifest);
        }
    }

    function createXlinkObject(url, parentElement, type, index, resolveType, originalContent) {
        return {
            url: url,
            parentElement: parentElement,
            type: type,
            index: index,
            resolveType: resolveType,
            originalContent: originalContent,
            resolvedContent: null,
            resolved: false
        };
    }

    // Check if all pending requests are finished
    function isResolvingFinished(elementsToResolve) {
        var i, obj;
        for (i = 0; i < elementsToResolve.elements.length; i++) {
            obj = elementsToResolve.elements[i];
            if (obj.resolved === false) {
                return false;
            }
        }
        return true;
    }

    // TODO : Do some syntax check here if the target is valid or not
    function isInappropriateTarget() {
        return false;
    }

    instance = {
        resolveManifestOnLoad: resolveManifestOnLoad,
        setMatchers: setMatchers,
        setIron: setIron,
        reset: reset
    };

    setup();
    return instance;
}

XlinkController.__dashjs_factory_name = 'XlinkController';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(XlinkController);
module.exports = exports['default'];

},{"../../../externals/xml2json.js":3,"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../XlinkLoader.js":45}],61:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var _dashModelsDashManifestModelJs = _dereq_('../../dash/models/DashManifestModel.js');

var _dashModelsDashManifestModelJs2 = _interopRequireDefault(_dashModelsDashManifestModelJs);

var _utilsObjectUtilsJs = _dereq_('../utils/ObjectUtils.js');

var _utilsObjectUtilsJs2 = _interopRequireDefault(_utilsObjectUtilsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var DEFAULT_INDEX = NaN;

var Node = function Node(_baseUrls, _selectedIdx) {
    _classCallCheck(this, Node);

    this.data = {
        baseUrls: _baseUrls || null,
        selectedIdx: _selectedIdx || DEFAULT_INDEX
    };
    this.children = [];
};

function BaseURLTreeModel() {

    var instance = undefined;
    var root = undefined;

    var context = this.context;
    var dashManifestModel = (0, _dashModelsDashManifestModelJs2['default'])(context).getInstance();
    var objectUtils = (0, _utilsObjectUtilsJs2['default'])(context).getInstance();

    function setup() {
        root = new Node();
    }

    function updateChildData(node, index, element) {
        var baseUrls = dashManifestModel.getBaseURLsFromElement(element);

        if (!node[index]) {
            node[index] = new Node(baseUrls);
        } else {
            if (!objectUtils.areSimpleEquivalent(baseUrls, node[index].data.baseUrls)) {
                node[index].data.baseUrls = baseUrls;
                node[index].data.selectedIdx = DEFAULT_INDEX;
            }
        }
    }

    function getBaseURLCollectionsFromManifest(manifest) {
        var baseUrls = dashManifestModel.getBaseURLsFromElement(manifest);

        if (!objectUtils.areSimpleEquivalent(baseUrls, root.data.baseUrls)) {
            root.data.baseUrls = baseUrls;
            root.data.selectedIdx = DEFAULT_INDEX;
        }

        if (manifest.Period_asArray) {
            manifest.Period_asArray.forEach(function (p, pi) {
                updateChildData(root.children, pi, p);

                if (p.AdaptationSet_asArray) {
                    p.AdaptationSet_asArray.forEach(function (a, ai) {
                        updateChildData(root.children[pi].children, ai, a);

                        if (a.Representation_asArray) {
                            a.Representation_asArray.sort(dashManifestModel.getRepresentationSortFunction()).forEach(function (r, ri) {
                                updateChildData(root.children[pi].children[ai].children, ri, r);
                            });
                        }
                    });
                }
            });
        }
    }

    function walk(callback, node) {
        var target = node || root;

        callback(target.data);

        if (target.children) {
            target.children.forEach(function (child) {
                return walk(callback, child);
            });
        }
    }

    function invalidateSelectedIndexes(serviceLocation) {
        walk(function (data) {
            if (!isNaN(data.selectedIdx)) {
                if (serviceLocation === data.baseUrls[data.selectedIdx].serviceLocation) {
                    data.selectedIdx = DEFAULT_INDEX;
                }
            }
        });
    }

    function update(manifest) {
        getBaseURLCollectionsFromManifest(manifest);
    }

    function reset() {
        root = new Node();
    }

    function getForPath(path) {
        var target = root;
        var nodes = [target.data];

        path.forEach(function (p) {
            target = target.children[p];

            if (target) {
                nodes.push(target.data);
            }
        });

        return nodes.filter(function (n) {
            return n.baseUrls.length;
        });
    }

    instance = {
        reset: reset,
        update: update,
        getForPath: getForPath,
        invalidateSelectedIndexes: invalidateSelectedIndexes
    };

    setup();

    return instance;
}

BaseURLTreeModel.__dashjs_factory_name = 'BaseURLTreeModel';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(BaseURLTreeModel);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7,"../../dash/models/DashManifestModel.js":17,"../utils/ObjectUtils.js":93}],62:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _voFragmentRequestJs = _dereq_('../vo/FragmentRequest.js');

var _voFragmentRequestJs2 = _interopRequireDefault(_voFragmentRequestJs);

var _coreDebugJs = _dereq_('../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var FRAGMENT_MODEL_LOADING = 'loading';
var FRAGMENT_MODEL_EXECUTED = 'executed';
var FRAGMENT_MODEL_CANCELED = 'canceled';
var FRAGMENT_MODEL_FAILED = 'failed';

function FragmentModel(config) {

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var metricsModel = config.metricsModel;

    var instance = undefined,
        scheduleController = undefined,
        executedRequests = undefined,
        loadingRequests = undefined,
        fragmentLoader = undefined;

    function setup() {
        scheduleController = null;
        fragmentLoader = null;
        executedRequests = [];
        loadingRequests = [];

        eventBus.on(_coreEventsEventsJs2['default'].LOADING_COMPLETED, onLoadingCompleted, instance);
    }

    function setLoader(value) {
        fragmentLoader = value;
    }

    function setScheduleController(value) {
        scheduleController = value;
    }

    function getScheduleController() {
        return scheduleController;
    }

    function isFragmentLoaded(request) {
        var isEqualComplete = function isEqualComplete(req1, req2) {
            return req1.action === _voFragmentRequestJs2['default'].ACTION_COMPLETE && req1.action === req2.action;
        };

        var isEqualMedia = function isEqualMedia(req1, req2) {
            return !isNaN(req1.index) && req1.index === req2.index && req1.startTime === req2.startTime && req1.adaptationIndex === req2.adaptationIndex;
        };

        var isEqualInit = function isEqualInit(req1, req2) {
            return isNaN(req1.index) && isNaN(req2.index) && req1.quality === req2.quality;
        };

        var check = function check(arr) {
            var req, i;
            var isLoaded = false;

            var ln = arr.length;

            for (i = 0; i < ln; i++) {
                req = arr[i];

                if (isEqualMedia(request, req) || isEqualInit(request, req) || isEqualComplete(request, req)) {
                    //log(request.mediaType + "Fragment already loaded for time: " + request.startTime);
                    isLoaded = true;
                    break;
                }
            }

            return isLoaded;
        };

        return check(loadingRequests) || check(executedRequests);
    }

    /**
     *
     * Gets an array of {@link FragmentRequest} objects
     *
     * @param {object} filter The object with properties by which the method filters the requests to be returned.
     *  the only mandatory property is state, which must be a value from
     *  other properties should match the properties of {@link FragmentRequest}. E.g.:
     *  getRequests({state: FragmentModel.FRAGMENT_MODEL_EXECUTED, quality: 0}) - returns
     *  all the requests from executedRequests array where requests.quality = filter.quality
     *
     * @returns {Array}
     * @memberof FragmentModel#
     */
    function getRequests(filter) {
        var requests = [];
        var filteredRequests = [];
        var ln = 1;
        var states;

        if (!filter || !filter.state) return requests;

        if (filter.state instanceof Array) {
            ln = filter.state.length;
            states = filter.state;
        } else {
            states = [filter.state];
        }

        for (var i = 0; i < ln; i++) {
            requests = getRequestsForState(states[i]);
            filteredRequests = filteredRequests.concat(filterRequests(requests, filter));
        }

        return filteredRequests;
    }

    function removeExecutedRequestsBeforeTime(time) {
        var lastIdx = executedRequests.length - 1;
        var start = NaN;
        var req = null;
        var i;

        // loop through the executed requests and remove the ones for which startTime is less than the given time
        for (i = lastIdx; i >= 0; i--) {
            req = executedRequests[i];
            start = req.startTime;
            if (!isNaN(start) && start < time) {
                removeRequest(executedRequests, req);
            }
        }
    }

    function abortRequests() {
        var reqs = [];
        fragmentLoader.abort();

        while (loadingRequests.length > 0) {
            reqs.push(loadingRequests[0]);
            removeRequest(loadingRequests, loadingRequests[0]);
        }

        loadingRequests = [];

        return reqs;
    }

    function executeRequest(request) {
        if (!request) return;

        switch (request.action) {
            case _voFragmentRequestJs2['default'].ACTION_COMPLETE:
                // Stream has completed, execute the corresponding callback
                executedRequests.push(request);
                addSchedulingInfoMetrics(request, FRAGMENT_MODEL_EXECUTED);
                eventBus.trigger(_coreEventsEventsJs2['default'].STREAM_COMPLETED, { request: request, fragmentModel: this });
                break;
            case _voFragmentRequestJs2['default'].ACTION_DOWNLOAD:
                addSchedulingInfoMetrics(request, FRAGMENT_MODEL_LOADING);
                loadingRequests.push(request);
                loadCurrentFragment(request);
                break;
            default:
                log('Unknown request action.');
        }
    }

    function reset() {
        eventBus.off(_coreEventsEventsJs2['default'].LOADING_COMPLETED, onLoadingCompleted, this);

        if (fragmentLoader) {
            fragmentLoader.reset();
            fragmentLoader = null;
        }

        context = null;
        executedRequests = [];
        loadingRequests = [];
    }

    function loadCurrentFragment(request) {
        eventBus.trigger(_coreEventsEventsJs2['default'].FRAGMENT_LOADING_STARTED, { sender: instance, request: request });
        fragmentLoader.load(request);
    }

    function removeRequest(arr, request) {
        var idx = arr.indexOf(request);

        if (idx !== -1) {
            arr.splice(idx, 1);
        }
    }

    function getRequestForTime(arr, time, threshold) {
        var lastIdx = arr.length - 1;
        var start = NaN;
        var end = NaN;
        var req = null;
        var i;

        // loop through the executed requests and pick the one for which the playback interval matches the given time
        for (i = lastIdx; i >= 0; i--) {
            req = arr[i];
            start = req.startTime;
            end = start + req.duration;
            threshold = threshold || req.duration / 2;
            if (!isNaN(start) && !isNaN(end) && time + threshold >= start && time - threshold < end || isNaN(start) && isNaN(time)) {
                return req;
            }
        }

        return null;
    }

    function filterRequests(arr, filter) {
        if (!filter) return arr;

        // for time use a specific filtration function
        if (filter.hasOwnProperty('time')) {
            return [getRequestForTime(arr, filter.time, filter.threshold)];
        }

        return arr.filter(function (request /*, idx, arr*/) {
            for (var prop in filter) {
                if (prop === 'state') continue;

                if (filter.hasOwnProperty(prop) && request[prop] != filter[prop]) return false;
            }

            return true;
        });
    }

    function getRequestsForState(state) {
        var requests;

        switch (state) {
            case FRAGMENT_MODEL_LOADING:
                requests = loadingRequests;
                break;
            case FRAGMENT_MODEL_EXECUTED:
                requests = executedRequests;
                break;
            default:
                requests = [];
        }

        return requests;
    }

    function addSchedulingInfoMetrics(request, state) {
        if (!request) return;

        var mediaType = request.mediaType;
        var now = new Date();
        var type = request.type;
        var startTime = request.startTime;
        var availabilityStartTime = request.availabilityStartTime;
        var duration = request.duration;
        var quality = request.quality;
        var range = request.range;

        metricsModel.addSchedulingInfo(mediaType, now, type, startTime, availabilityStartTime, duration, quality, range, state);
        metricsModel.addRequestsQueue(mediaType, loadingRequests, executedRequests);
    }

    function onLoadingCompleted(e) {
        if (e.sender !== fragmentLoader) return;

        var request = e.request;
        var response = e.response;
        var error = e.error;

        loadingRequests.splice(loadingRequests.indexOf(request), 1);

        if (response && !error) {
            executedRequests.push(request);
        }

        addSchedulingInfoMetrics(request, error ? FRAGMENT_MODEL_FAILED : FRAGMENT_MODEL_EXECUTED);
        eventBus.trigger(_coreEventsEventsJs2['default'].FRAGMENT_LOADING_COMPLETED, { request: request, response: response, error: error, sender: this });
    }

    instance = {
        setLoader: setLoader,
        setScheduleController: setScheduleController,
        getScheduleController: getScheduleController,
        getRequests: getRequests,
        isFragmentLoaded: isFragmentLoaded,
        removeExecutedRequestsBeforeTime: removeExecutedRequestsBeforeTime,
        abortRequests: abortRequests,
        executeRequest: executeRequest,
        reset: reset
    };

    setup();
    return instance;
}

FragmentModel.__dashjs_factory_name = 'FragmentModel';
var factory = _coreFactoryMakerJs2['default'].getClassFactory(FragmentModel);
factory.FRAGMENT_MODEL_LOADING = FRAGMENT_MODEL_LOADING;
factory.FRAGMENT_MODEL_EXECUTED = FRAGMENT_MODEL_EXECUTED;
factory.FRAGMENT_MODEL_CANCELED = FRAGMENT_MODEL_CANCELED;
factory.FRAGMENT_MODEL_FAILED = FRAGMENT_MODEL_FAILED;
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/Debug.js":5,"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../vo/FragmentRequest.js":101}],63:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function ManifestModel() {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        manifest = undefined;

    function getValue() {
        return manifest;
    }

    function setValue(value) {
        manifest = value;
        eventBus.trigger(_coreEventsEventsJs2['default'].MANIFEST_LOADED, { data: value });
    }

    instance = {
        getValue: getValue,
        setValue: setValue
    };

    return instance;
}

ManifestModel.__dashjs_factory_name = 'ManifestModel';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(ManifestModel);
module.exports = exports['default'];

},{"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9}],64:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _voMetricsHTTPRequestJs = _dereq_('../vo/metrics/HTTPRequest.js');

var _voMetricsHTTPRequestJs2 = _interopRequireDefault(_voMetricsHTTPRequestJs);

var DEFAULT_UTC_TIMING_SOURCE = { scheme: 'urn:mpeg:dash:utc:http-xsdate:2014', value: 'http://time.akamai.com/?iso' };
var LIVE_DELAY_FRAGMENT_COUNT = 4;

var DEFAULT_LOCAL_STORAGE_BITRATE_EXPIRATION = 360000;
var DEFAULT_LOCAL_STORAGE_MEDIA_SETTINGS_EXPIRATION = 360000;

var BANDWIDTH_SAFETY_FACTOR = 0.9;
var ABANDON_LOAD_TIMEOUT = 10000;

var BUFFER_TO_KEEP = 40;
var BUFFER_PRUNING_INTERVAL = 30;
var DEFAULT_MIN_BUFFER_TIME = 40;
var BUFFER_TIME_AT_TOP_QUALITY = 40;
var BUFFER_TIME_AT_TOP_QUALITY_LONG_FORM = 60;
var LONG_FORM_CONTENT_DURATION_THRESHOLD = 600;
var RICH_BUFFER_THRESHOLD = 20;

var FRAGMENT_RETRY_ATTEMPTS = 3;
var FRAGMENT_RETRY_INTERVAL = 1000;

var MANIFEST_RETRY_ATTEMPTS = 3;
var MANIFEST_RETRY_INTERVAL = 1000;

var XLINK_RETRY_ATTEMPTS = 1;
var XLINK_RETRY_INTERVAL = 500;

//This value influences the startup time for live (in ms).
var WALLCLOCK_TIME_UPDATE_INTERVAL = 50;

function MediaPlayerModel() {

    var instance = undefined,
        useManifestDateHeaderTimeSource = undefined,
        useSuggestedPresentationDelay = undefined,
        UTCTimingSources = undefined,
        liveDelayFragmentCount = undefined,
        liveDelay = undefined,
        scheduleWhilePaused = undefined,
        bufferToKeep = undefined,
        bufferPruningInterval = undefined,
        lastBitrateCachingInfo = undefined,
        lastMediaSettingsCachingInfo = undefined,
        stableBufferTime = undefined,
        bufferTimeAtTopQuality = undefined,
        bufferTimeAtTopQualityLongForm = undefined,
        longFormContentDurationThreshold = undefined,
        richBufferThreshold = undefined,
        bandwidthSafetyFactor = undefined,
        abandonLoadTimeout = undefined,
        retryAttempts = undefined,
        retryIntervals = undefined,
        wallclockTimeUpdateInterval = undefined,
        bufferOccupancyABREnabled = undefined;

    function setup() {
        var _retryAttempts, _retryIntervals;

        UTCTimingSources = [];
        useSuggestedPresentationDelay = false;
        useManifestDateHeaderTimeSource = true;
        scheduleWhilePaused = true;
        bufferOccupancyABREnabled = false;
        lastBitrateCachingInfo = { enabled: true, ttl: DEFAULT_LOCAL_STORAGE_BITRATE_EXPIRATION };
        lastMediaSettingsCachingInfo = { enabled: true, ttl: DEFAULT_LOCAL_STORAGE_MEDIA_SETTINGS_EXPIRATION };
        liveDelayFragmentCount = LIVE_DELAY_FRAGMENT_COUNT;
        liveDelay = undefined; // Explicitly state that default is undefined
        bufferToKeep = BUFFER_TO_KEEP;
        bufferPruningInterval = BUFFER_PRUNING_INTERVAL;
        stableBufferTime = DEFAULT_MIN_BUFFER_TIME;
        bufferTimeAtTopQuality = BUFFER_TIME_AT_TOP_QUALITY;
        bufferTimeAtTopQualityLongForm = BUFFER_TIME_AT_TOP_QUALITY_LONG_FORM;
        longFormContentDurationThreshold = LONG_FORM_CONTENT_DURATION_THRESHOLD;
        richBufferThreshold = RICH_BUFFER_THRESHOLD;
        bandwidthSafetyFactor = BANDWIDTH_SAFETY_FACTOR;
        abandonLoadTimeout = ABANDON_LOAD_TIMEOUT;
        wallclockTimeUpdateInterval = WALLCLOCK_TIME_UPDATE_INTERVAL;

        retryAttempts = (_retryAttempts = {}, _defineProperty(_retryAttempts, _voMetricsHTTPRequestJs2['default'].MPD_TYPE, MANIFEST_RETRY_ATTEMPTS), _defineProperty(_retryAttempts, _voMetricsHTTPRequestJs2['default'].XLINK_EXPANSION_TYPE, XLINK_RETRY_ATTEMPTS), _defineProperty(_retryAttempts, _voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE, FRAGMENT_RETRY_ATTEMPTS), _defineProperty(_retryAttempts, _voMetricsHTTPRequestJs2['default'].INIT_SEGMENT_TYPE, FRAGMENT_RETRY_ATTEMPTS), _defineProperty(_retryAttempts, _voMetricsHTTPRequestJs2['default'].BITSTREAM_SWITCHING_SEGMENT_TYPE, FRAGMENT_RETRY_ATTEMPTS), _defineProperty(_retryAttempts, _voMetricsHTTPRequestJs2['default'].INDEX_SEGMENT_TYPE, FRAGMENT_RETRY_ATTEMPTS), _defineProperty(_retryAttempts, _voMetricsHTTPRequestJs2['default'].OTHER_TYPE, FRAGMENT_RETRY_ATTEMPTS), _retryAttempts);

        retryIntervals = (_retryIntervals = {}, _defineProperty(_retryIntervals, _voMetricsHTTPRequestJs2['default'].MPD_TYPE, MANIFEST_RETRY_INTERVAL), _defineProperty(_retryIntervals, _voMetricsHTTPRequestJs2['default'].XLINK_EXPANSION_TYPE, XLINK_RETRY_INTERVAL), _defineProperty(_retryIntervals, _voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE, FRAGMENT_RETRY_INTERVAL), _defineProperty(_retryIntervals, _voMetricsHTTPRequestJs2['default'].INIT_SEGMENT_TYPE, FRAGMENT_RETRY_INTERVAL), _defineProperty(_retryIntervals, _voMetricsHTTPRequestJs2['default'].BITSTREAM_SWITCHING_SEGMENT_TYPE, FRAGMENT_RETRY_INTERVAL), _defineProperty(_retryIntervals, _voMetricsHTTPRequestJs2['default'].INDEX_SEGMENT_TYPE, FRAGMENT_RETRY_INTERVAL), _defineProperty(_retryIntervals, _voMetricsHTTPRequestJs2['default'].OTHER_TYPE, FRAGMENT_RETRY_INTERVAL), _retryIntervals);
    }

    //TODO Should we use Object.define to have setters/getters? makes more readable code on other side.
    function setBufferOccupancyABREnabled(value) {
        bufferOccupancyABREnabled = value;
    }

    function getBufferOccupancyABREnabled() {
        return bufferOccupancyABREnabled;
    }

    function setBandwidthSafetyFactor(value) {
        bandwidthSafetyFactor = value;
    }

    function getBandwidthSafetyFactor() {
        return bandwidthSafetyFactor;
    }

    function setAbandonLoadTimeout(value) {
        abandonLoadTimeout = value;
    }

    function getAbandonLoadTimeout() {
        return abandonLoadTimeout;
    }

    function setStableBufferTime(value) {
        stableBufferTime = value;
    }

    function getStableBufferTime() {
        return stableBufferTime;
    }

    function setBufferTimeAtTopQuality(value) {
        bufferTimeAtTopQuality = value;
    }

    function getBufferTimeAtTopQuality() {
        return bufferTimeAtTopQuality;
    }

    function setBufferTimeAtTopQualityLongForm(value) {
        bufferTimeAtTopQualityLongForm = value;
    }

    function getBufferTimeAtTopQualityLongForm() {
        return bufferTimeAtTopQualityLongForm;
    }

    function setLongFormContentDurationThreshold(value) {
        longFormContentDurationThreshold = value;
    }

    function getLongFormContentDurationThreshold() {
        return longFormContentDurationThreshold;
    }

    function setRichBufferThreshold(value) {
        richBufferThreshold = value;
    }

    function getRichBufferThreshold() {
        return richBufferThreshold;
    }

    function setBufferToKeep(value) {
        bufferToKeep = value;
    }

    function getBufferToKeep() {
        return bufferToKeep;
    }

    function setLastBitrateCachingInfo(enable, ttl) {
        lastBitrateCachingInfo.enabled = enable;
        if (ttl !== undefined && !isNaN(ttl) && typeof ttl === 'number') {
            lastBitrateCachingInfo.ttl = ttl;
        }
    }

    function getLastBitrateCachingInfo() {
        return lastBitrateCachingInfo;
    }

    function setLastMediaSettingsCachingInfo(enable, ttl) {
        lastMediaSettingsCachingInfo.enabled = enable;
        if (ttl !== undefined && !isNaN(ttl) && typeof ttl === 'number') {
            lastMediaSettingsCachingInfo.ttl = ttl;
        }
    }

    function getLastMediaSettingsCachingInfo() {
        return lastMediaSettingsCachingInfo;
    }

    function setBufferPruningInterval(value) {
        bufferPruningInterval = value;
    }

    function getBufferPruningInterval() {
        return bufferPruningInterval;
    }

    function setFragmentRetryAttempts(value) {
        retryAttempts[_voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE] = value;
    }

    function setRetryAttemptsForType(type, value) {
        retryAttempts[type] = value;
    }

    function getFragmentRetryAttempts() {
        return retryAttempts[_voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE];
    }

    function getRetryAttemptsForType(type) {
        return retryAttempts[type];
    }

    function setFragmentRetryInterval(value) {
        retryIntervals[_voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE] = value;
    }

    function setRetryIntervalForType(type, value) {
        retryIntervals[type] = value;
    }

    function getFragmentRetryInterval() {
        return retryIntervals[_voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE];
    }

    function getRetryIntervalForType(type) {
        return retryIntervals[type];
    }

    function setWallclockTimeUpdateInterval(value) {
        wallclockTimeUpdateInterval = value;
    }

    function getWallclockTimeUpdateInterval() {
        return wallclockTimeUpdateInterval;
    }

    function setScheduleWhilePaused(value) {
        scheduleWhilePaused = value;
    }

    function getScheduleWhilePaused() {
        return scheduleWhilePaused;
    }

    function setLiveDelayFragmentCount(value) {
        liveDelayFragmentCount = value;
    }

    function setLiveDelay(value) {
        liveDelay = value;
    }

    function getLiveDelayFragmentCount() {
        return liveDelayFragmentCount;
    }

    function getLiveDelay() {
        return liveDelay;
    }

    function setUseManifestDateHeaderTimeSource(value) {
        useManifestDateHeaderTimeSource = value;
    }

    function getUseManifestDateHeaderTimeSource() {
        return useManifestDateHeaderTimeSource;
    }

    function setUseSuggestedPresentationDelay(value) {
        useSuggestedPresentationDelay = value;
    }

    function getUseSuggestedPresentationDelay() {
        return useSuggestedPresentationDelay;
    }

    function setUTCTimingSources(value) {
        UTCTimingSources = value;
    }

    function getUTCTimingSources() {
        return UTCTimingSources;
    }

    function reset() {
        //TODO need to figure out what props to persist across sessions and which to reset if any.
        //setup();
    }

    instance = {
        setBufferOccupancyABREnabled: setBufferOccupancyABREnabled,
        getBufferOccupancyABREnabled: getBufferOccupancyABREnabled,
        setBandwidthSafetyFactor: setBandwidthSafetyFactor,
        getBandwidthSafetyFactor: getBandwidthSafetyFactor,
        setAbandonLoadTimeout: setAbandonLoadTimeout,
        getAbandonLoadTimeout: getAbandonLoadTimeout,
        setLastBitrateCachingInfo: setLastBitrateCachingInfo,
        getLastBitrateCachingInfo: getLastBitrateCachingInfo,
        setLastMediaSettingsCachingInfo: setLastMediaSettingsCachingInfo,
        getLastMediaSettingsCachingInfo: getLastMediaSettingsCachingInfo,
        setStableBufferTime: setStableBufferTime,
        getStableBufferTime: getStableBufferTime,
        setBufferTimeAtTopQuality: setBufferTimeAtTopQuality,
        getBufferTimeAtTopQuality: getBufferTimeAtTopQuality,
        setBufferTimeAtTopQualityLongForm: setBufferTimeAtTopQualityLongForm,
        getBufferTimeAtTopQualityLongForm: getBufferTimeAtTopQualityLongForm,
        setLongFormContentDurationThreshold: setLongFormContentDurationThreshold,
        getLongFormContentDurationThreshold: getLongFormContentDurationThreshold,
        setRichBufferThreshold: setRichBufferThreshold,
        getRichBufferThreshold: getRichBufferThreshold,
        setBufferToKeep: setBufferToKeep,
        getBufferToKeep: getBufferToKeep,
        setBufferPruningInterval: setBufferPruningInterval,
        getBufferPruningInterval: getBufferPruningInterval,
        setFragmentRetryAttempts: setFragmentRetryAttempts,
        getFragmentRetryAttempts: getFragmentRetryAttempts,
        setRetryAttemptsForType: setRetryAttemptsForType,
        getRetryAttemptsForType: getRetryAttemptsForType,
        setFragmentRetryInterval: setFragmentRetryInterval,
        getFragmentRetryInterval: getFragmentRetryInterval,
        setRetryIntervalForType: setRetryIntervalForType,
        getRetryIntervalForType: getRetryIntervalForType,
        setWallclockTimeUpdateInterval: setWallclockTimeUpdateInterval,
        getWallclockTimeUpdateInterval: getWallclockTimeUpdateInterval,
        setScheduleWhilePaused: setScheduleWhilePaused,
        getScheduleWhilePaused: getScheduleWhilePaused,
        getUseSuggestedPresentationDelay: getUseSuggestedPresentationDelay,
        setUseSuggestedPresentationDelay: setUseSuggestedPresentationDelay,
        setLiveDelayFragmentCount: setLiveDelayFragmentCount,
        getLiveDelayFragmentCount: getLiveDelayFragmentCount,
        getLiveDelay: getLiveDelay,
        setLiveDelay: setLiveDelay,
        setUseManifestDateHeaderTimeSource: setUseManifestDateHeaderTimeSource,
        getUseManifestDateHeaderTimeSource: getUseManifestDateHeaderTimeSource,
        setUTCTimingSources: setUTCTimingSources,
        getUTCTimingSources: getUTCTimingSources,
        reset: reset
    };

    setup();

    return instance;
}

//TODO see if you can move this and not export and just getter to get default value.
MediaPlayerModel.__dashjs_factory_name = 'MediaPlayerModel';
var factory = _coreFactoryMakerJs2['default'].getSingletonFactory(MediaPlayerModel);
factory.DEFAULT_UTC_TIMING_SOURCE = DEFAULT_UTC_TIMING_SOURCE;
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7,"../vo/metrics/HTTPRequest.js":117}],65:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _voMetricsListJs = _dereq_('../vo/MetricsList.js');

var _voMetricsListJs2 = _interopRequireDefault(_voMetricsListJs);

var _voMetricsTCPConnectionJs = _dereq_('../vo/metrics/TCPConnection.js');

var _voMetricsTCPConnectionJs2 = _interopRequireDefault(_voMetricsTCPConnectionJs);

var _voMetricsHTTPRequestJs = _dereq_('../vo/metrics/HTTPRequest.js');

var _voMetricsHTTPRequestJs2 = _interopRequireDefault(_voMetricsHTTPRequestJs);

var _voMetricsRepresentationSwitchJs = _dereq_('../vo/metrics/RepresentationSwitch.js');

var _voMetricsRepresentationSwitchJs2 = _interopRequireDefault(_voMetricsRepresentationSwitchJs);

var _voMetricsBufferLevelJs = _dereq_('../vo/metrics/BufferLevel.js');

var _voMetricsBufferLevelJs2 = _interopRequireDefault(_voMetricsBufferLevelJs);

var _voMetricsBufferStateJs = _dereq_('../vo/metrics/BufferState.js');

var _voMetricsBufferStateJs2 = _interopRequireDefault(_voMetricsBufferStateJs);

var _voMetricsDVRInfoJs = _dereq_('../vo/metrics/DVRInfo.js');

var _voMetricsDVRInfoJs2 = _interopRequireDefault(_voMetricsDVRInfoJs);

var _voMetricsDroppedFramesJs = _dereq_('../vo/metrics/DroppedFrames.js');

var _voMetricsDroppedFramesJs2 = _interopRequireDefault(_voMetricsDroppedFramesJs);

var _voMetricsManifestUpdateJs = _dereq_('../vo/metrics/ManifestUpdate.js');

var _voMetricsManifestUpdateJs2 = _interopRequireDefault(_voMetricsManifestUpdateJs);

var _voMetricsSchedulingInfoJs = _dereq_('../vo/metrics/SchedulingInfo.js');

var _voMetricsSchedulingInfoJs2 = _interopRequireDefault(_voMetricsSchedulingInfoJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _voMetricsRequestsQueueJs = _dereq_('../vo/metrics/RequestsQueue.js');

var _voMetricsRequestsQueueJs2 = _interopRequireDefault(_voMetricsRequestsQueueJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _voMetricsBolaStateJs = _dereq_('../vo/metrics/BolaState.js');

var _voMetricsBolaStateJs2 = _interopRequireDefault(_voMetricsBolaStateJs);

function MetricsModel() {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        adapter = undefined,
        streamMetrics = undefined;

    function setup() {
        streamMetrics = {};
    }

    function setConfig(config) {
        if (!config) return;

        if (config.adapter) {
            adapter = config.adapter;
        }
    }

    function metricsChanged() {
        eventBus.trigger(_coreEventsEventsJs2['default'].METRICS_CHANGED);
    }

    function metricChanged(mediaType) {
        eventBus.trigger(_coreEventsEventsJs2['default'].METRIC_CHANGED, { mediaType: mediaType });
        metricsChanged();
    }

    function metricUpdated(mediaType, metricType, vo) {
        eventBus.trigger(_coreEventsEventsJs2['default'].METRIC_UPDATED, { mediaType: mediaType, metric: metricType, value: vo });
        metricChanged(mediaType);
    }

    function metricAdded(mediaType, metricType, vo) {
        eventBus.trigger(_coreEventsEventsJs2['default'].METRIC_ADDED, { mediaType: mediaType, metric: metricType, value: vo });
        metricChanged(mediaType);
    }

    function clearCurrentMetricsForType(type) {
        delete streamMetrics[type];
        metricChanged(type);
    }

    function clearAllCurrentMetrics() {
        streamMetrics = {};
        metricsChanged();
    }

    function getReadOnlyMetricsFor(type) {
        if (streamMetrics.hasOwnProperty(type)) {
            return streamMetrics[type];
        }

        return null;
    }

    function getMetricsFor(type) {
        var metrics;

        if (streamMetrics.hasOwnProperty(type)) {
            metrics = streamMetrics[type];
        } else {
            metrics = new _voMetricsListJs2['default']();
            streamMetrics[type] = metrics;
        }

        return metrics;
    }

    function addTcpConnection(mediaType, tcpid, dest, topen, tclose, tconnect) {
        var vo = new _voMetricsTCPConnectionJs2['default']();

        vo.tcpid = tcpid;
        vo.dest = dest;
        vo.topen = topen;
        vo.tclose = tclose;
        vo.tconnect = tconnect;

        getMetricsFor(mediaType).TcpList.push(vo);

        metricAdded(mediaType, adapter.metricsList.TCP_CONNECTION, vo);
        return vo;
    }

    function appendHttpTrace(httpRequest, s, d, b) {
        var vo = new _voMetricsHTTPRequestJs2['default'].Trace();

        vo.s = s;
        vo.d = d;
        vo.b = b;

        httpRequest.trace.push(vo);

        if (!httpRequest.interval) {
            httpRequest.interval = 0;
        }

        httpRequest.interval += d;

        return vo;
    }

    function addHttpRequest(mediaType, tcpid, type, url, actualurl, serviceLocation, range, trequest, tresponse, tfinish, responsecode, mediaduration, responseHeaders, traces) {
        var vo = new _voMetricsHTTPRequestJs2['default']();

        // ISO 23009-1 D.4.3 NOTE 2:
        // All entries for a given object will have the same URL and range
        // and so can easily be correlated. If there were redirects or
        // failures there will be one entry for each redirect/failure.
        // The redirect-to URL or alternative url (where multiple have been
        // provided in the MPD) will appear as the actualurl of the next
        // entry with the same url value.
        if (actualurl && actualurl !== url) {

            // given the above, add an entry for the original request
            addHttpRequest(mediaType, null, type, url, null, null, range, trequest, null, // unknown
            null, // unknown
            null, // unknown, probably a 302
            mediaduration, null, null);

            vo.actualurl = actualurl;
        }

        vo.tcpid = tcpid;
        vo.type = type;
        vo.url = url;
        vo.range = range;
        vo.trequest = trequest;
        vo.tresponse = tresponse;
        vo.responsecode = responsecode;

        vo._tfinish = tfinish;
        vo._stream = mediaType;
        vo._mediaduration = mediaduration;
        vo._responseHeaders = responseHeaders;
        vo._serviceLocation = serviceLocation;

        if (traces) {
            traces.forEach(function (trace) {
                appendHttpTrace(vo, trace.s, trace.d, trace.b);
            });
        } else {
            // The interval and trace shall be absent for redirect and failure records.
            delete vo.interval;
            delete vo.trace;
        }

        getMetricsFor(mediaType).HttpList.push(vo);

        metricAdded(mediaType, adapter.metricsList.HTTP_REQUEST, vo);
        return vo;
    }

    function addRepresentationSwitch(mediaType, t, mt, to, lto) {
        var vo = new _voMetricsRepresentationSwitchJs2['default']();

        vo.t = t;
        vo.mt = mt;
        vo.to = to;

        if (lto) {
            vo.lto = lto;
        } else {
            delete vo.lto;
        }

        getMetricsFor(mediaType).RepSwitchList.push(vo);

        metricAdded(mediaType, adapter.metricsList.TRACK_SWITCH, vo);
        return vo;
    }

    function addBufferLevel(mediaType, t, level) {
        var vo = new _voMetricsBufferLevelJs2['default']();
        vo.t = t;
        vo.level = level;
        getMetricsFor(mediaType).BufferLevel.push(vo);

        metricAdded(mediaType, adapter.metricsList.BUFFER_LEVEL, vo);
        return vo;
    }

    function addBufferState(mediaType, state, target) {
        var vo = new _voMetricsBufferStateJs2['default']();
        vo.target = target;
        vo.state = state;
        getMetricsFor(mediaType).BufferState.push(vo);

        metricAdded(mediaType, adapter.metricsList.BUFFER_STATE, vo);
        return vo;
    }

    function addDVRInfo(mediaType, currentTime, mpd, range) {
        var vo = new _voMetricsDVRInfoJs2['default']();

        vo.time = currentTime;
        vo.range = range;
        vo.manifestInfo = mpd;

        getMetricsFor(mediaType).DVRInfo.push(vo);
        metricAdded(mediaType, adapter.metricsList.DVR_INFO, vo);

        return vo;
    }

    function addDroppedFrames(mediaType, quality) {
        var vo = new _voMetricsDroppedFramesJs2['default']();
        var list = getMetricsFor(mediaType).DroppedFrames;

        vo.time = quality.creationTime;
        vo.droppedFrames = quality.droppedVideoFrames;

        if (list.length > 0 && list[list.length - 1] == vo) {
            return list[list.length - 1];
        }

        list.push(vo);

        metricAdded(mediaType, adapter.metricsList.DROPPED_FRAMES, vo);
        return vo;
    }

    function addSchedulingInfo(mediaType, t, type, startTime, availabilityStartTime, duration, quality, range, state) {
        var vo = new _voMetricsSchedulingInfoJs2['default']();

        vo.mediaType = mediaType;
        vo.t = t;

        vo.type = type;
        vo.startTime = startTime;
        vo.availabilityStartTime = availabilityStartTime;
        vo.duration = duration;
        vo.quality = quality;
        vo.range = range;

        vo.state = state;

        getMetricsFor(mediaType).SchedulingInfo.push(vo);

        metricAdded(mediaType, adapter.metricsList.SCHEDULING_INFO, vo);
        return vo;
    }

    function addRequestsQueue(mediaType, loadingRequests, executedRequests) {
        var vo = new _voMetricsRequestsQueueJs2['default']();
        vo.loadingRequests = loadingRequests;
        vo.executedRequests = executedRequests;

        getMetricsFor(mediaType).RequestsQueue = vo;
        metricAdded(mediaType, adapter.metricsList.REQUESTS_QUEUE, vo);
    }

    function addManifestUpdate(mediaType, type, requestTime, fetchTime, availabilityStartTime, presentationStartTime, clientTimeOffset, currentTime, buffered, latency) {
        var vo = new _voMetricsManifestUpdateJs2['default']();
        var metrics = getMetricsFor('stream');

        vo.mediaType = mediaType;
        vo.type = type;
        vo.requestTime = requestTime; // when this manifest update was requested
        vo.fetchTime = fetchTime; // when this manifest update was received
        vo.availabilityStartTime = availabilityStartTime;
        vo.presentationStartTime = presentationStartTime; // the seek point (liveEdge for dynamic, Stream[0].startTime for static)
        vo.clientTimeOffset = clientTimeOffset; // the calculated difference between the server and client wall clock time
        vo.currentTime = currentTime; // actual element.currentTime
        vo.buffered = buffered; // actual element.ranges
        vo.latency = latency; // (static is fixed value of zero. dynamic should be ((Now-@availabilityStartTime) - currentTime)

        metrics.ManifestUpdate.push(vo);
        metricAdded(mediaType, adapter.metricsList.MANIFEST_UPDATE, vo);

        return vo;
    }

    function updateManifestUpdateInfo(manifestUpdate, updatedFields) {
        if (manifestUpdate) {
            for (var field in updatedFields) {
                manifestUpdate[field] = updatedFields[field];
            }

            metricUpdated(manifestUpdate.mediaType, adapter.metricsList.MANIFEST_UPDATE, manifestUpdate);
        }
    }

    function addManifestUpdateStreamInfo(manifestUpdate, id, index, start, duration) {
        if (manifestUpdate) {
            var vo = new _voMetricsManifestUpdateJs2['default'].StreamInfo();

            vo.id = id;
            vo.index = index;
            vo.start = start;
            vo.duration = duration;

            manifestUpdate.streamInfo.push(vo);
            metricUpdated(manifestUpdate.mediaType, adapter.metricsList.MANIFEST_UPDATE_STREAM_INFO, manifestUpdate);

            return vo;
        }
        return null;
    }

    function addManifestUpdateRepresentationInfo(manifestUpdate, id, index, streamIndex, mediaType, presentationTimeOffset, startNumber, fragmentInfoType) {
        if (manifestUpdate) {
            var vo = new _voMetricsManifestUpdateJs2['default'].TrackInfo();

            vo.id = id;
            vo.index = index;
            vo.streamIndex = streamIndex;
            vo.mediaType = mediaType;
            vo.startNumber = startNumber;
            vo.fragmentInfoType = fragmentInfoType;
            vo.presentationTimeOffset = presentationTimeOffset;

            manifestUpdate.trackInfo.push(vo);
            metricUpdated(manifestUpdate.mediaType, adapter.metricsList.MANIFEST_UPDATE_TRACK_INFO, manifestUpdate);

            return vo;
        }
        return null;
    }

    function addPlayList(vo) {
        var type = 'stream';

        if (vo.trace && Array.isArray(vo.trace)) {
            vo.trace.forEach(function (trace) {
                if (trace.hasOwnProperty('subreplevel') && !trace.subreplevel) {
                    delete trace.subreplevel;
                }
            });
        } else {
            delete vo.trace;
        }

        getMetricsFor(type).PlayList.push(vo);

        metricAdded(type, adapter.metricsList.PLAY_LIST, vo);
        return vo;
    }

    function addDVBErrors(vo) {
        var type = 'stream';

        getMetricsFor(type).DVBErrors.push(vo);

        metricAdded(type, adapter.metricsList.DVB_ERRORS, vo);

        return vo;
    }

    function updateBolaState(mediaType, _s) {
        var vo = new _voMetricsBolaStateJs2['default']();
        vo._s = _s;
        getMetricsFor(mediaType).BolaState = [vo];

        metricAdded(mediaType, 'BolaState', vo);
        return vo;
    }

    instance = {
        metricsChanged: metricsChanged,
        metricChanged: metricChanged,
        metricUpdated: metricUpdated,
        metricAdded: metricAdded,
        clearCurrentMetricsForType: clearCurrentMetricsForType,
        clearAllCurrentMetrics: clearAllCurrentMetrics,
        getReadOnlyMetricsFor: getReadOnlyMetricsFor,
        getMetricsFor: getMetricsFor,
        addTcpConnection: addTcpConnection,
        addHttpRequest: addHttpRequest,
        addRepresentationSwitch: addRepresentationSwitch,
        addBufferLevel: addBufferLevel,
        addBufferState: addBufferState,
        addDVRInfo: addDVRInfo,
        addDroppedFrames: addDroppedFrames,
        addSchedulingInfo: addSchedulingInfo,
        addRequestsQueue: addRequestsQueue,
        addManifestUpdate: addManifestUpdate,
        updateManifestUpdateInfo: updateManifestUpdateInfo,
        addManifestUpdateStreamInfo: addManifestUpdateStreamInfo,
        addManifestUpdateRepresentationInfo: addManifestUpdateRepresentationInfo,
        addPlayList: addPlayList,
        addDVBErrors: addDVBErrors,
        updateBolaState: updateBolaState,
        setConfig: setConfig
    };

    setup();
    return instance;
}

MetricsModel.__dashjs_factory_name = 'MetricsModel';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(MetricsModel);
module.exports = exports['default'];

},{"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../vo/MetricsList.js":106,"../vo/metrics/BolaState.js":112,"../vo/metrics/BufferLevel.js":113,"../vo/metrics/BufferState.js":114,"../vo/metrics/DVRInfo.js":115,"../vo/metrics/DroppedFrames.js":116,"../vo/metrics/HTTPRequest.js":117,"../vo/metrics/ManifestUpdate.js":118,"../vo/metrics/RepresentationSwitch.js":120,"../vo/metrics/RequestsQueue.js":121,"../vo/metrics/SchedulingInfo.js":122,"../vo/metrics/TCPConnection.js":123}],66:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _voURIFragmentDataJs = _dereq_('../vo/URIFragmentData.js');

var _voURIFragmentDataJs2 = _interopRequireDefault(_voURIFragmentDataJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function URIQueryAndFragmentModel() {

    var instance = undefined,
        URIFragmentDataVO = undefined,
        URIQueryData = undefined,
        isHTTPS = undefined;

    function initialize() {
        URIFragmentDataVO = new _voURIFragmentDataJs2['default']();
        URIQueryData = [];
        isHTTPS = false;
    }

    function getURIFragmentData() {
        return URIFragmentDataVO;
    }

    function getURIQueryData() {
        return URIQueryData;
    }

    function isManifestHTTPS() {
        return isHTTPS;
    }

    function parseURI(uri) {
        if (!uri) return null;

        var URIFragmentData = [];
        var mappedArr;

        var testQuery = new RegExp(/[?]/);
        var testFragment = new RegExp(/[#]/);
        var testHTTPS = new RegExp(/^(https:)?\/\//i);
        var isQuery = testQuery.test(uri);
        var isFragment = testFragment.test(uri);

        isHTTPS = testHTTPS.test(uri);

        function reduceArray(previousValue, currentValue, index, array) {
            var arr = array[0].split(/[=]/);
            array.push({ key: arr[0], value: arr[1] });
            array.shift();
            return array;
        }

        function mapArray(currentValue, index, array) {
            if (index > 0) {
                if (isQuery && URIQueryData.length === 0) {
                    URIQueryData = array[index].split(/[&]/);
                } else if (isFragment) {
                    URIFragmentData = array[index].split(/[&]/);
                }
            }

            return array;
        }

        mappedArr = uri.split(/[?#]/).map(mapArray);

        if (URIQueryData.length > 0) {
            URIQueryData = URIQueryData.reduce(reduceArray, null);
        }

        if (URIFragmentData.length > 0) {
            URIFragmentData = URIFragmentData.reduce(reduceArray, null);
            URIFragmentData.forEach(function (object) {
                URIFragmentDataVO[object.key] = object.value;
            });
        }

        return uri;
    }

    instance = {
        initialize: initialize,
        parseURI: parseURI,
        getURIFragmentData: getURIFragmentData,
        getURIQueryData: getURIQueryData,
        isManifestHTTPS: isManifestHTTPS
    };

    return instance;
}

URIQueryAndFragmentModel.__dashjs_factory_name = 'URIQueryAndFragmentModel';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(URIQueryAndFragmentModel);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7,"../vo/URIFragmentData.js":111}],67:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function VideoModel() {

    var instance = undefined,
        element = undefined,
        TTMLRenderingDiv = undefined,
        videoContainer = undefined,
        stalledStreams = undefined,
        previousPlaybackRate = undefined;

    function initialize() {
        stalledStreams = [];
    }

    function setPlaybackRate(value) {
        if (!element || element.readyState < 2) return;
        element.playbackRate = value;
    }

    //TODO Move the DVR window calculations from MediaPlayer to Here.
    function setCurrentTime(currentTime) {
        //_currentTime = currentTime;

        // We don't set the same currentTime because it can cause firing unexpected Pause event in IE11
        // providing playbackRate property equals to zero.
        if (element.currentTime == currentTime) return;

        // TODO Despite the fact that MediaSource 'open' event has been fired IE11 cannot set videoElement.currentTime
        // immediately (it throws InvalidStateError). It seems that this is related to videoElement.readyState property
        // Initially it is 0, but soon after 'open' event it goes to 1 and setting currentTime is allowed. Chrome allows to
        // set currentTime even if readyState = 0.
        // setTimeout is used to workaround InvalidStateError in IE11
        try {
            element.currentTime = currentTime;
        } catch (e) {
            if (element.readyState === 0 && e.code === e.INVALID_STATE_ERR) {
                setTimeout(function () {
                    element.currentTime = currentTime;
                }, 400);
            }
        }
    }

    function getElement() {
        return element;
    }

    function setElement(value) {
        element = value;
        // Workaround to force Firefox to fire the canplay event.
        element.preload = 'auto';
    }

    function setSource(source) {
        if (source) {
            element.src = source;
        } else {
            element.removeAttribute('src');
            element.load();
        }
    }

    function getSource() {
        return element.src;
    }

    function getVideoContainer() {
        return videoContainer;
    }

    function setVideoContainer(value) {
        videoContainer = value;
    }

    function getTTMLRenderingDiv() {
        return TTMLRenderingDiv;
    }

    function setTTMLRenderingDiv(div) {
        TTMLRenderingDiv = div;
        // The styling will allow the captions to match the video window size and position.
        TTMLRenderingDiv.style.position = 'absolute';
        TTMLRenderingDiv.style.display = 'flex';
        TTMLRenderingDiv.style.overflow = 'hidden';
        TTMLRenderingDiv.style.pointerEvents = 'none';
        TTMLRenderingDiv.style.top = 0;
        TTMLRenderingDiv.style.left = 0;
    }

    function setStallState(type, state) {
        stallStream(type, state);
    }

    function isStalled() {
        return stalledStreams.length > 0;
    }

    function addStalledStream(type) {

        var event = undefined;

        if (type === null || element.seeking || stalledStreams.indexOf(type) !== -1) {
            return;
        }

        stalledStreams.push(type);
        if (stalledStreams.length === 1) {
            // Halt playback until nothing is stalled.
            event = document.createEvent('Event');
            event.initEvent('waiting', true, false);
            previousPlaybackRate = element.playbackRate;
            setPlaybackRate(0);
            element.dispatchEvent(event);
        }
    }

    function removeStalledStream(type) {
        var index = stalledStreams.indexOf(type);
        var event = undefined;

        if (type === null) {
            return;
        }
        if (index !== -1) {
            stalledStreams.splice(index, 1);
        }
        // If nothing is stalled resume playback.
        if (isStalled() === false && element.playbackRate === 0) {
            event = document.createEvent('Event');
            event.initEvent('playing', true, false);
            setPlaybackRate(previousPlaybackRate || 1);
            element.dispatchEvent(event);
        }
    }

    function stallStream(type, isStalled) {
        if (isStalled) {
            addStalledStream(type);
        } else {
            removeStalledStream(type);
        }
    }

    function getPlaybackQuality() {
        var hasWebKit = ('webkitDroppedFrameCount' in element);
        var hasQuality = ('getVideoPlaybackQuality' in element);
        var result = null;

        if (hasQuality) {
            result = element.getVideoPlaybackQuality();
        } else if (hasWebKit) {
            result = { droppedVideoFrames: element.webkitDroppedFrameCount, creationTime: new Date() };
        }

        return result;
    }

    instance = {
        initialize: initialize,
        setCurrentTime: setCurrentTime,
        setStallState: setStallState,
        getElement: getElement,
        setElement: setElement,
        setSource: setSource,
        getSource: getSource,
        getVideoContainer: getVideoContainer,
        setVideoContainer: setVideoContainer,
        getTTMLRenderingDiv: getTTMLRenderingDiv,
        setTTMLRenderingDiv: setTTMLRenderingDiv,
        getPlaybackQuality: getPlaybackQuality
    };

    return instance;
}

VideoModel.__dashjs_factory_name = 'VideoModel';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(VideoModel);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7}],68:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function RulesContext(config) {

    var instance = undefined;
    var representationInfo = config.streamProcessor.getCurrentRepresentationInfo();
    var sp = config.streamProcessor;
    var currentValue = config.currentValue;

    function getStreamInfo() {
        return representationInfo.mediaInfo.streamInfo;
    }

    function getMediaInfo() {
        return representationInfo.mediaInfo;
    }

    function getTrackInfo() {
        return representationInfo;
    }

    function getCurrentValue() {
        return currentValue;
    }

    function getManifestInfo() {
        return representationInfo.mediaInfo.streamInfo.manifestInfo;
    }

    function getStreamProcessor() {
        return sp;
    }

    instance = {
        getStreamInfo: getStreamInfo,
        getMediaInfo: getMediaInfo,
        getTrackInfo: getTrackInfo,
        getCurrentValue: getCurrentValue,
        getManifestInfo: getManifestInfo,
        getStreamProcessor: getStreamProcessor
    };

    return instance;
}

RulesContext.__dashjs_factory_name = 'RulesContext';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(RulesContext);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7}],69:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _RulesContextJs = _dereq_('./RulesContext.js');

var _RulesContextJs2 = _interopRequireDefault(_RulesContextJs);

var _SwitchRequestJs = _dereq_('./SwitchRequest.js');

var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs);

var _abrABRRulesCollectionJs = _dereq_('./abr/ABRRulesCollection.js');

var _abrABRRulesCollectionJs2 = _interopRequireDefault(_abrABRRulesCollectionJs);

var _synchronizationSynchronizationRulesCollectionJs = _dereq_('./synchronization/SynchronizationRulesCollection.js');

var _synchronizationSynchronizationRulesCollectionJs2 = _interopRequireDefault(_synchronizationSynchronizationRulesCollectionJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var ABR_RULE = 0;
var SYNC_RULE = 1;

function RulesController() {

    var context = this.context;

    var instance = undefined,
        rules = undefined;

    function initialize() {
        rules = {};
    }

    function setConfig(config) {
        if (!config) return;

        if (config.abrRulesCollection) {
            rules[ABR_RULE] = config.abrRulesCollection;
        }

        if (config.synchronizationRulesCollection) {
            rules[SYNC_RULE] = config.synchronizationRulesCollection;
        }
    }

    function applyRules(rulesArr, streamProcessor, callback, current, overrideFunc) {
        var values = {};
        var rule, i;

        var rulesCount = rulesArr.length;
        var ln = rulesCount;
        var rulesContext = getRulesContext(streamProcessor, current);

        var callbackFunc = function callbackFunc(result) {
            var value, confidence;

            if (result.value !== _SwitchRequestJs2['default'].NO_CHANGE) {
                values[result.priority] = overrideFunc(values[result.priority], result.value);
            }

            if (--rulesCount) return;

            if (values[_SwitchRequestJs2['default'].WEAK] !== _SwitchRequestJs2['default'].NO_CHANGE) {
                confidence = _SwitchRequestJs2['default'].WEAK;
                value = values[_SwitchRequestJs2['default'].WEAK];
            }

            if (values[_SwitchRequestJs2['default'].DEFAULT] !== _SwitchRequestJs2['default'].NO_CHANGE) {
                confidence = _SwitchRequestJs2['default'].DEFAULT;
                value = values[_SwitchRequestJs2['default'].DEFAULT];
            }

            if (values[_SwitchRequestJs2['default'].STRONG] !== _SwitchRequestJs2['default'].NO_CHANGE) {
                confidence = _SwitchRequestJs2['default'].STRONG;
                value = values[_SwitchRequestJs2['default'].STRONG];
            }

            if (confidence != _SwitchRequestJs2['default'].STRONG && confidence != _SwitchRequestJs2['default'].WEAK) {
                confidence = _SwitchRequestJs2['default'].DEFAULT;
            }

            callback({ value: value !== undefined ? value : current, confidence: confidence });
        };

        values[_SwitchRequestJs2['default'].STRONG] = _SwitchRequestJs2['default'].NO_CHANGE;
        values[_SwitchRequestJs2['default'].WEAK] = _SwitchRequestJs2['default'].NO_CHANGE;
        values[_SwitchRequestJs2['default'].DEFAULT] = _SwitchRequestJs2['default'].NO_CHANGE;

        for (i = 0; i < ln; i++) {
            rule = rulesArr[i];
            rule.execute(rulesContext, callbackFunc);
        }
    }

    function reset() {
        var abrRules = rules[ABR_RULE];
        var synchronizationRules = rules[SYNC_RULE];
        var allRules = (abrRules.getRules(_abrABRRulesCollectionJs2['default'].QUALITY_SWITCH_RULES) || []).concat(abrRules.getRules(_abrABRRulesCollectionJs2['default'].ABANDON_FRAGMENT_RULES) || []).concat(synchronizationRules.getRules(_synchronizationSynchronizationRulesCollectionJs2['default'].TIME_SYNCHRONIZED_RULES) || []).concat(synchronizationRules.getRules(_synchronizationSynchronizationRulesCollectionJs2['default'].BEST_GUESS_RULES) || []);
        var ln = allRules.length;

        var rule, i;

        for (i = 0; i < ln; i++) {
            rule = allRules[i];

            if (typeof rule.reset !== 'function') continue;

            rule.reset();
        }

        rules = {};
    }

    function getRulesContext(streamProcessor, currentValue) {
        return (0, _RulesContextJs2['default'])(context).create({ streamProcessor: streamProcessor, currentValue: currentValue });
    }

    instance = {
        initialize: initialize,
        setConfig: setConfig,
        applyRules: applyRules,
        reset: reset
    };

    return instance;
}

RulesController.__dashjs_factory_name = 'RulesController';
var factory = _coreFactoryMakerJs2['default'].getSingletonFactory(RulesController);
factory.ABR_RULE = ABR_RULE;
factory.SYNC_RULE = SYNC_RULE;
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7,"./RulesContext.js":68,"./SwitchRequest.js":70,"./abr/ABRRulesCollection.js":71,"./synchronization/SynchronizationRulesCollection.js":84}],70:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var NO_CHANGE = 999;
var DEFAULT = 0.5;
var STRONG = 1;
var WEAK = 0;

function SwitchRequest(v, p) {
    //TODO refactor all the calls to this to use config to be like everything else.
    var value = v === undefined ? NO_CHANGE : v;
    var priority = p === undefined ? DEFAULT : p;

    var instance = {
        value: value,
        priority: priority
    };

    return instance;
}

SwitchRequest.__dashjs_factory_name = 'SwitchRequest';
var factory = _coreFactoryMakerJs2['default'].getClassFactory(SwitchRequest);
factory.NO_CHANGE = NO_CHANGE;
factory.DEFAULT = DEFAULT;
factory.STRONG = STRONG;
factory.WEAK = WEAK;
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7}],71:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _ThroughputRuleJs = _dereq_('./ThroughputRule.js');

var _ThroughputRuleJs2 = _interopRequireDefault(_ThroughputRuleJs);

var _BufferOccupancyRuleJs = _dereq_('./BufferOccupancyRule.js');

var _BufferOccupancyRuleJs2 = _interopRequireDefault(_BufferOccupancyRuleJs);

var _InsufficientBufferRuleJs = _dereq_('./InsufficientBufferRule.js');

var _InsufficientBufferRuleJs2 = _interopRequireDefault(_InsufficientBufferRuleJs);

var _AbandonRequestsRuleJs = _dereq_('./AbandonRequestsRule.js');

var _AbandonRequestsRuleJs2 = _interopRequireDefault(_AbandonRequestsRuleJs);

var _BolaRuleJs = _dereq_('./BolaRule.js');

var _BolaRuleJs2 = _interopRequireDefault(_BolaRuleJs);

var _BolaAbandonRuleJs = _dereq_('./BolaAbandonRule.js');

var _BolaAbandonRuleJs2 = _interopRequireDefault(_BolaAbandonRuleJs);

var _modelsMediaPlayerModelJs = _dereq_('../../models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

var _modelsMetricsModelJs = _dereq_('../../models/MetricsModel.js');

var _modelsMetricsModelJs2 = _interopRequireDefault(_modelsMetricsModelJs);

var _dashDashMetricsJs = _dereq_('../../../dash/DashMetrics.js');

var _dashDashMetricsJs2 = _interopRequireDefault(_dashDashMetricsJs);

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var QUALITY_SWITCH_RULES = 'qualitySwitchRules';
var ABANDON_FRAGMENT_RULES = 'abandonFragmentRules';

function ABRRulesCollection() {

    var context = this.context;
    var isWebOS = !!window.PalmSystem;

    var instance = undefined,
        qualitySwitchRules = undefined,
        abandonFragmentRules = undefined;

    function initialize() {
        qualitySwitchRules = [];
        abandonFragmentRules = [];

        var metricsModel = (0, _modelsMetricsModelJs2['default'])(context).getInstance();
        var dashMetrics = (0, _dashDashMetricsJs2['default'])(context).getInstance();
        var mediaPlayerModel = (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance();

        if (!isWebOS && mediaPlayerModel.getBufferOccupancyABREnabled()) {
            qualitySwitchRules.push((0, _BolaRuleJs2['default'])(context).create({
                metricsModel: metricsModel,
                dashMetrics: (0, _dashDashMetricsJs2['default'])(context).getInstance()
            }));
            abandonFragmentRules.push((0, _BolaAbandonRuleJs2['default'])(context).create({
                metricsModel: metricsModel,
                dashMetrics: (0, _dashDashMetricsJs2['default'])(context).getInstance()
            }));
        } else {
            qualitySwitchRules.push((0, _ThroughputRuleJs2['default'])(context).create({
                metricsModel: metricsModel,
                dashMetrics: dashMetrics
            }));

            // disable BufferOccupancy rule for webOS as it not always is able
            // to play topmost quality
            if (!isWebOS) {
                qualitySwitchRules.push((0, _BufferOccupancyRuleJs2['default'])(context).create({
                    metricsModel: metricsModel,
                    dashMetrics: dashMetrics
                }));
            }

            qualitySwitchRules.push((0, _InsufficientBufferRuleJs2['default'])(context).create({ metricsModel: metricsModel }));
            abandonFragmentRules.push((0, _AbandonRequestsRuleJs2['default'])(context).create());
        }
    }

    function getRules(type) {
        switch (type) {
            case QUALITY_SWITCH_RULES:
                return qualitySwitchRules;
            case ABANDON_FRAGMENT_RULES:
                return abandonFragmentRules;
            default:
                return null;
        }
    }

    instance = {
        initialize: initialize,
        getRules: getRules
    };

    return instance;
}

ABRRulesCollection.__dashjs_factory_name = 'ABRRulesCollection';
var factory = _coreFactoryMakerJs2['default'].getSingletonFactory(ABRRulesCollection);
factory.QUALITY_SWITCH_RULES = QUALITY_SWITCH_RULES;
factory.ABANDON_FRAGMENT_RULES = ABANDON_FRAGMENT_RULES;
exports['default'] = factory;
module.exports = exports['default'];

},{"../../../core/FactoryMaker.js":7,"../../../dash/DashMetrics.js":13,"../../models/MediaPlayerModel.js":64,"../../models/MetricsModel.js":65,"./AbandonRequestsRule.js":72,"./BolaAbandonRule.js":73,"./BolaRule.js":74,"./BufferOccupancyRule.js":75,"./InsufficientBufferRule.js":76,"./ThroughputRule.js":77}],72:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _SwitchRequestJs = _dereq_('../SwitchRequest.js');

var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs);

var _modelsMediaPlayerModelJs = _dereq_('../../models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var GRACE_TIME_THRESHOLD = 500;
var ABANDON_MULTIPLIER = 1.5;

function AbandonRequestsRule() /*config*/{

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;

    var instance = undefined,
        fragmentDict = undefined,
        abandonDict = undefined,
        mediaPlayerModel = undefined;

    function setup() {
        fragmentDict = {};
        abandonDict = {};
        mediaPlayerModel = (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance();
    }

    function setFragmentRequestDict(type, id) {
        fragmentDict[type] = fragmentDict[type] || {};
        fragmentDict[type][id] = fragmentDict[type][id] || {};
    }

    function execute(rulesContext, callback) {
        var fragmentInfo;
        var now = new Date().getTime();
        var mediaInfo = rulesContext.getMediaInfo();
        var mediaType = mediaInfo.type;
        var progressEvent = rulesContext.getCurrentValue();
        var representationInfo = rulesContext.getTrackInfo();
        var req = progressEvent.request;
        var abrController = rulesContext.getStreamProcessor().getABRController();
        var switchRequest = (0, _SwitchRequestJs2['default'])(context).create(_SwitchRequestJs2['default'].NO_CHANGE, _SwitchRequestJs2['default'].WEAK);

        if (!isNaN(req.index)) {
            setFragmentRequestDict(mediaType, req.index);
            fragmentInfo = fragmentDict[mediaType][req.index];

            if (fragmentInfo === null || req.firstByteDate === null || abandonDict.hasOwnProperty(fragmentInfo.id)) {
                callback(switchRequest);
                return;
            }

            //setup some init info based on first progress event
            if (fragmentInfo.firstByteTime === undefined) {
                fragmentInfo.firstByteTime = req.firstByteDate.getTime();
                fragmentInfo.segmentDuration = req.duration;
                fragmentInfo.bytesTotal = req.bytesTotal;
                fragmentInfo.id = req.index;
                //log("FRAG ID : " ,fragmentInfo.id, " *****************");
            }
            //update info base on subsequent progress events until completed.
            fragmentInfo.bytesLoaded = req.bytesLoaded;
            fragmentInfo.elapsedTime = now - fragmentInfo.firstByteTime;

            if (fragmentInfo.bytesLoaded < fragmentInfo.bytesTotal && fragmentInfo.elapsedTime >= GRACE_TIME_THRESHOLD) {

                fragmentInfo.measuredBandwidthInKbps = Math.round(fragmentInfo.bytesLoaded * 8 / fragmentInfo.elapsedTime);
                fragmentInfo.estimatedTimeOfDownload = (fragmentInfo.bytesTotal * 8 * 0.001 / fragmentInfo.measuredBandwidthInKbps).toFixed(2);
                //log("id: ",fragmentInfo.id,  "kbps: ", fragmentInfo.measuredBandwidthInKbps, "etd: ",fragmentInfo.estimatedTimeOfDownload, "et: ", fragmentInfo.elapsedTime/1000);

                if (fragmentInfo.estimatedTimeOfDownload < fragmentInfo.segmentDuration * ABANDON_MULTIPLIER || representationInfo.quality === 0) {
                    callback(switchRequest);
                    return;
                } else if (!abandonDict.hasOwnProperty(fragmentInfo.id)) {
                    var newQuality = abrController.getQualityForBitrate(mediaInfo, fragmentInfo.measuredBandwidthInKbps * mediaPlayerModel.getBandwidthSafetyFactor());
                    switchRequest = (0, _SwitchRequestJs2['default'])(context).create(newQuality, _SwitchRequestJs2['default'].STRONG);
                    abandonDict[fragmentInfo.id] = fragmentInfo;
                    log('AbandonRequestsRule ( ', mediaType, 'frag id', fragmentInfo.id, ') is asking to abandon and switch to quality to ', newQuality, ' measured bandwidth was', fragmentInfo.measuredBandwidthInKbps);
                    delete fragmentDict[mediaType][fragmentInfo.id];
                }
            } else if (fragmentInfo.bytesLoaded === fragmentInfo.bytesTotal) {
                delete fragmentDict[mediaType][fragmentInfo.id];
            }
        }

        callback(switchRequest);
    }

    function reset() {
        fragmentDict = {};
        abandonDict = {};
    }

    instance = {
        execute: execute,
        reset: reset
    };

    setup();

    return instance;
}

AbandonRequestsRule.__dashjs_factory_name = 'AbandonRequestsRule';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(AbandonRequestsRule);
module.exports = exports['default'];

},{"../../../core/Debug.js":5,"../../../core/FactoryMaker.js":7,"../../models/MediaPlayerModel.js":64,"../SwitchRequest.js":70}],73:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2016, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _SwitchRequestJs = _dereq_('../SwitchRequest.js');

var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs);

var _modelsMediaPlayerModelJs = _dereq_('../../models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var _BolaRuleJs = _dereq_('./BolaRule.js');

var _BolaRuleJs2 = _interopRequireDefault(_BolaRuleJs);

function BolaAbandonRule(config) {

    // do not abandon during the grace period
    var GRACE_PERIOD_MS = 500;
    var POOR_RTT_MS = 200;

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var dashMetrics = config.dashMetrics;
    var metricsModel = config.metricsModel;

    var instance = undefined,
        abandonDict = undefined,
        mediaPlayerModel = undefined;

    function setup() {
        abandonDict = {};
        mediaPlayerModel = (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance();
    }

    function rememberAbandon(mediaType, index, quality) {
        // if this is called, then canStillAbandon(mediaType, index, quality) should have returned true
        abandonDict[mediaType] = { index: index, quality: quality };
    }

    function canStillAbandon(mediaType, index, quality) {
        var adm = abandonDict[mediaType];
        if (!adm) return true;
        return index > adm.index || index == adm.index && quality < adm.quality;
    }

    function execute(rulesContext, callback) {
        var mediaInfo = rulesContext.getMediaInfo();
        var mediaType = mediaInfo.type;
        var metrics = metricsModel.getReadOnlyMetricsFor(mediaType);
        var progressEvent = rulesContext.getCurrentValue();
        var request = progressEvent.request;
        var switchRequest = (0, _SwitchRequestJs2['default'])(context).create(_SwitchRequestJs2['default'].NO_CHANGE, _SwitchRequestJs2['default'].WEAK);

        // TODO: should we abandon during startup?
        if (metrics.BolaState.length === 0 || metrics.BolaState[0]._s.state !== _BolaRuleJs2['default'].BOLA_STATE_STEADY) {
            abandonDict = {}; // otherwise seek back might disable abandonment - TODO: handle cleaner
            callback(switchRequest);
            return;
        }

        var bolaState = metrics.BolaState[0]._s;
        // TODO: does changing bolaState conform to coding style, or should we clone?

        var index = request.index;
        var quality = request.quality;

        if (isNaN(index) || quality === 0 || !canStillAbandon(mediaType, index, quality) || !request.firstByteDate) {
            callback(switchRequest);
            return;
        }

        var nowMilliSeconds = new Date().getTime();
        var elapsedTimeMilliSeconds = nowMilliSeconds - request.firstByteDate.getTime();

        var bytesLoaded = request.bytesLoaded;
        var bytesTotal = request.bytesTotal;
        var bytesRemaining = bytesTotal - bytesLoaded;
        var duration = request.duration;

        var bufferLevel = dashMetrics.getCurrentBufferLevel(metrics) ? dashMetrics.getCurrentBufferLevel(metrics) : 0.0;

        var estimateThroughputBSF = bolaState.bandwidthSafetyFactor * 8000.0 * bytesLoaded / elapsedTimeMilliSeconds; // throughput in bits per second
        var rttSeconds = 0.001 * (request.firstByteDate.getTime() - request.requestStartDate.getTime());
        if (rttSeconds < 0.001 * POOR_RTT_MS) rttSeconds = 0.001 * POOR_RTT_MS;

        var diagnosticMessage = 'index=' + index + ' quality=' + quality + ' bytesLoaded/bytesTotal=' + bytesLoaded + '/' + bytesTotal + ' bufferLevel=' + bufferLevel + ' timeSince1stByte=' + (elapsedTimeMilliSeconds / 1000).toFixed(3) + ' estThroughput=' + (estimateThroughputBSF / 1000000).toFixed(3) + ' latency=' + rttSeconds.toFixed(3);

        var estimateOtherBytesTotal = bytesTotal * bolaState.bitrate[0] / bolaState.bitrate[quality];
        var estimateBytesRemainingAfterRtt = bytesRemaining - rttSeconds * estimateThroughputBSF / 8.0;
        if (estimateBytesRemainingAfterRtt < 1.0) {
            estimateBytesRemainingAfterRtt = 1.0;
        }

        if (elapsedTimeMilliSeconds < GRACE_PERIOD_MS || bytesRemaining <= estimateOtherBytesTotal || bufferLevel > bolaState.bufferTarget || estimateBytesRemainingAfterRtt <= estimateOtherBytesTotal || 8 * bytesTotal / estimateThroughputBSF <= duration) {
            // Do not abandon during first GRACE_PERIOD_MS.
            // Do not abandon if we need to download less bytes than the size of the lowest quality fragment.
            // Do not abandon if buffer level is above bufferTarget because the schedule controller will not download anything anyway.
            // Do not abandon if after rttSeconds bytesRemaining is estimated to drop below size of lowest quality fragment.
            // Do not abandon if fragment takes less than 1 fragment duration to download.
            callback(switchRequest);
            return;
        }

        // check if we are giving the safety guarantee (see comment in BolaRule.js)
        if (bolaState.safetyGuarantee && bufferLevel <= bolaState.fragmentDuration && bolaState.state === _BolaRuleJs2['default'].BOLA_STATE_STEADY) {
            // If the buffer only has one fragment left, then this is the last chance to abandon without rebuffering if the network bandwidth corresponds to the lowest bitrate.

            // BOLA_STATE_STEADY: During startup the buffer has not yet grown enough and will give false positives.

            bolaState.lastQuality = 0;
            metricsModel.updateBolaState(mediaType, bolaState);

            rememberAbandon(mediaType, index, quality);
            switchRequest = (0, _SwitchRequestJs2['default'])(context).create(0, _SwitchRequestJs2['default'].STRONG);
            if (_BolaRuleJs2['default'].BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaAbandonRule to 0 for safety guarantee' + ' - ' + diagnosticMessage);
            callback(switchRequest);
            return;
        }

        var estimateTimeRemainSeconds = 8.0 * bytesRemaining / estimateThroughputBSF;

        // find maximum allowed quality that shouldn't lead to rebuffering
        var maxQualityAllowed = quality; // recall that quality > 0; if quality === 0 then we would have returned early
        if (estimateTimeRemainSeconds > bufferLevel) {
            --maxQualityAllowed;
            while (maxQualityAllowed > 0) {
                estimateOtherBytesTotal = bytesTotal * bolaState.bitrate[maxQualityAllowed] / bolaState.bitrate[quality];
                estimateTimeRemainSeconds = rttSeconds + 8 * estimateOtherBytesTotal / estimateThroughputBSF;
                if (estimateTimeRemainSeconds <= bufferLevel) {
                    break;
                }
                --maxQualityAllowed;
            }
        }

        // If we abandon, there will be RTT time before we get first byte at lower quality.
        // By that time, the no-abandon option would have downloaded some more, and the buffer level would have depleted some more.
        // Introducing this RTT cushion also helps avoid extra abandonment, especially with close bitrates.

        var bufferAfterRtt = bufferLevel + bolaState.virtualBuffer - rttSeconds;

        // check if we should abandon using BOLA utility criteria
        var newQuality = quality;
        var score = (bolaState.utility[quality] + bolaState.gp - bufferAfterRtt / bolaState.Vp) / estimateBytesRemainingAfterRtt;

        for (var i = 0; i < quality; ++i) {
            estimateOtherBytesTotal = bytesTotal * bolaState.bitrate[i] / bolaState.bitrate[quality];
            if (estimateOtherBytesTotal > estimateBytesRemainingAfterRtt) {
                // abandoning to download at q such that i <= q < quality is not helpful
                break;
            }
            var s = (bolaState.utility[i] + bolaState.gp - bufferAfterRtt / bolaState.Vp) / estimateOtherBytesTotal;
            if (s > score) {
                newQuality = i;
                score = s;
            }
        }

        // compare with maximum allowed quality that shouldn't lead to rebuffering
        if (newQuality > maxQualityAllowed) {
            newQuality = maxQualityAllowed;
        }

        if (newQuality === quality) {
            // no change
            callback(switchRequest);
            return;
        }

        // Abandon, but to which quality? Abandoning should not happen often, and it's OK to be more conservative when it does.
        while (newQuality > 0 && bolaState.bitrate[newQuality] > estimateThroughputBSF) {
            --newQuality;
        }

        bolaState.lastQuality = newQuality;
        metricsModel.updateBolaState(mediaType, bolaState);

        rememberAbandon(mediaType, index, quality);
        switchRequest = (0, _SwitchRequestJs2['default'])(context).create(newQuality, _SwitchRequestJs2['default'].STRONG);
        if (_BolaRuleJs2['default'].BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaAbandonRule abandon to ' + newQuality + ' - ' + diagnosticMessage);
        callback(switchRequest);
    }

    function reset() {
        abandonDict = {};
    }

    instance = {
        execute: execute,
        reset: reset
    };

    setup();

    return instance;
}

BolaAbandonRule.__dashjs_factory_name = 'BolaAbandonRule';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(BolaAbandonRule);
module.exports = exports['default'];

},{"../../../core/Debug.js":5,"../../../core/FactoryMaker.js":7,"../../models/MediaPlayerModel.js":64,"../SwitchRequest.js":70,"./BolaRule.js":74}],74:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2016, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

// For a description of the BOLA adaptive bitrate (ABR) algorithm, see http://arxiv.org/abs/1601.06748

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _SwitchRequestJs = _dereq_('../SwitchRequest.js');

var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs);

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _modelsMediaPlayerModelJs = _dereq_('../../models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

var _controllersPlaybackControllerJs = _dereq_('../../controllers/PlaybackController.js');

var _controllersPlaybackControllerJs2 = _interopRequireDefault(_controllersPlaybackControllerJs);

var _voMetricsHTTPRequestJs = _dereq_('../../vo/metrics/HTTPRequest.js');

var _voMetricsHTTPRequestJs2 = _interopRequireDefault(_voMetricsHTTPRequestJs);

var _dashDashAdapterJs = _dereq_('../../../dash/DashAdapter.js');

var _dashDashAdapterJs2 = _interopRequireDefault(_dashDashAdapterJs);

var _coreEventBusJs = _dereq_('../../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreDebugJs = _dereq_('../../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

// BOLA_STATE_ONE_BITRATE   : If there is only one bitrate (or initialization failed), always return NO_CHANGE.
// BOLA_STATE_STARTUP       : Download fragments at most recently measured throughput.
// BOLA_STATE_STARTUP_NO_INC: If quality increased then decreased during startup, then quality cannot be increased.
// BOLA_STATE_STEADY        : Buffer primed, we switch to steady operation.
// TODO: add BOLA_STATE_SEEK and tune Bola behavior on seeking
var BOLA_STATE_ONE_BITRATE = 0;
var BOLA_STATE_STARTUP = 1;
var BOLA_STATE_STARTUP_NO_INC = 2;
var BOLA_STATE_STEADY = 3;
var BOLA_DEBUG = false; // TODO: remove

function BolaRule(config) {

    // Bola needs some space between buffer levels.
    var MINIMUM_BUFFER_LEVEL_SPACING = 5.0;

    var AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_LIVE = 2;
    var AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_VOD = 3;

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var dashMetrics = config.dashMetrics;
    var metricsModel = config.metricsModel;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        lastCallTimeDict = undefined,
        seekMediaTypes = undefined,
        mediaPlayerModel = undefined,
        playbackController = undefined,
        adapter = undefined;

    function setup() {
        lastCallTimeDict = {};
        seekMediaTypes = [];
        mediaPlayerModel = (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance();
        playbackController = (0, _controllersPlaybackControllerJs2['default'])(context).getInstance();
        adapter = (0, _dashDashAdapterJs2['default'])(context).getInstance();
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, instance);
        eventBus.on(_coreEventsEventsJs2['default'].PERIOD_SWITCH_STARTED, onPeriodSwitchStarted, instance);
    }

    function calculateInitialState(rulesContext) {
        // TODO: analyze behavior of weird inputs and handle as gracefully as possible

        // TODO: currently based on 12 second buffer target, tweek to utilize a higher buffer target

        var initialState = {};

        var mediaInfo = rulesContext.getMediaInfo();

        var bitrate = mediaInfo.bitrateList.map(function (b) {
            return b.bandwidth;
        });
        var bitrateCount = bitrate.length;
        if (bitrateCount < 2 || bitrate[0] >= bitrate[1] || bitrate[bitrateCount - 2] >= bitrate[bitrateCount - 1]) {
            // if bitrate list irregular, stick to lowest bitrate
            // TODO: should we tolerate repeated bitrates?
            initialState.state = BOLA_STATE_ONE_BITRATE;
            return initialState;
        }

        var streamProcessor = rulesContext.getStreamProcessor();
        var streamInfo = rulesContext.getStreamInfo();
        var trackInfo = rulesContext.getTrackInfo();

        var isDynamic = streamProcessor.isDynamic();
        var duration = streamInfo.manifestInfo.duration;
        var fragmentDuration = trackInfo.fragmentDuration;

        var bufferTarget = undefined;
        var bufferMax = undefined;
        // Note: If isDynamic (live streaming) we keep the same target for cases where the user is playing behind live edge, but then make throughput-based decisions when the buffer level is low because of availability.
        bufferTarget = mediaPlayerModel.getStableBufferTime();
        if (duration >= mediaPlayerModel.getLongFormContentDurationThreshold()) {
            bufferMax = mediaPlayerModel.getBufferTimeAtTopQualityLongForm();
        } else {
            bufferMax = mediaPlayerModel.getBufferTimeAtTopQuality();
        }

        // During live streaming, there might not be enough fragments available to fill all the way to the buffer target. In such a case, Bola detects the lack of fragment availability and calculate a bitrate depending on what the buffer level would have been had more fragments been available. This is done by keeping an additional virtualBuffer level. Of course, in such a case Bola needs to also keep track of the real buffer to avoid rebuffering.

        // Bola needs some space between buffer levels. If bolaBufferTarget is set to a level higher than the real bufferTarget, the Schedule Controller will still not fill up the buffer up to bolaBufferTarget. However, Bola will detect the effect of the Schedule Controller and calculate a bitrate depending on what the buffer level would have been had the Schedule Controller filled more buffer. This is handled similar to the live streaming scenario using the additional virtualBuffer level.
        var bolaBufferTarget = bufferTarget;
        if (bolaBufferTarget < fragmentDuration + MINIMUM_BUFFER_LEVEL_SPACING) {

            bolaBufferTarget = fragmentDuration + MINIMUM_BUFFER_LEVEL_SPACING;
        }

        var utility = [];
        for (var i = 0; i < bitrateCount; ++i) {
            utility.push(Math.log(bitrate[i] / bitrate[0]));
        }

        // BOLA parameters V and gamma (multiplied by p === fragmentDuration):
        // Choose Vp and gp such that logarithmic utility would always prefer the lowest bitrate when bufferLevel === fragmentDuration and would always prefer the highest bitrate when bufferLevel === bufferTarget.
        // TODO: document the math
        var Vp = (bolaBufferTarget - fragmentDuration) / utility[bitrateCount - 1];
        var gp = 1.0 + utility[bitrateCount - 1] / (bolaBufferTarget / fragmentDuration - 1.0);

        // If the bufferTarget (the real bufferTarget and not bolaBufferTarget) is large enough, we might guarantee that Bola will never rebuffer unless the network bandwidth drops below the lowest encoded bitrate level. For this to work Bola needs to use the real buffer level without the additional virtualBuffer. Also, for this to work efficiently, we need to make sure that if the buffer level drops to one fragment during a download, the current download does not have more bits remaining than the size of one fragment at the lowest quality.
        var maxRtt = 0.2; // TODO: is this reasonable?
        var safetyGuarantee = !isDynamic && bolaBufferTarget === bufferTarget;
        if (safetyGuarantee) {
            // TODO: document the math
            // we might need to adjust Vp and gp
            var VpNew = Vp;
            var gpNew = gp;
            for (var i = 1; i < bitrateCount; ++i) {
                var threshold = VpNew * (gpNew - bitrate[0] * utility[i] / (bitrate[i] - bitrate[0]));
                var minThreshold = fragmentDuration * (2.0 - bitrate[0] / bitrate[i]) + maxRtt;
                if (minThreshold >= bufferTarget) {
                    safetyGuarantee = false;
                    break;
                }
                if (threshold < minThreshold) {
                    VpNew *= (bufferTarget - minThreshold) / (bufferTarget - threshold);
                    gpNew = minThreshold / VpNew + utility[i] * bitrate[0] / (bitrate[i] - bitrate[0]);
                }
            }
            if (safetyGuarantee && (bufferTarget - fragmentDuration) * VpNew / Vp < MINIMUM_BUFFER_LEVEL_SPACING) {
                safetyGuarantee = false;
            }
            if (safetyGuarantee) {
                Vp = VpNew;
                gp = gpNew;
            }
        }

        // When using the virtualBuffer, it must be capped.
        // TODO: document the math
        var bolaBufferMax = Vp * (utility[bitrateCount - 1] + gp);

        // Note: We either use the virtualBuffer or the safetyGuarantee, but not both.

        initialState.state = BOLA_STATE_STARTUP;

        initialState.bitrate = bitrate;
        initialState.utility = utility;
        initialState.Vp = Vp;
        initialState.gp = gp;

        initialState.fragmentDuration = fragmentDuration;
        initialState.bandwidthSafetyFactor = mediaPlayerModel.getBandwidthSafetyFactor();
        initialState.bufferTarget = bufferTarget;
        initialState.bufferMax = bufferMax;
        initialState.bolaBufferTarget = bolaBufferTarget;
        initialState.bolaBufferMax = bolaBufferMax;

        initialState.isDynamic = isDynamic;
        initialState.safetyGuarantee = safetyGuarantee;
        initialState.lastQuality = 0;
        initialState.virtualBuffer = 0.0;
        initialState.throughputCount = isDynamic ? AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_LIVE : AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_VOD;

        if (BOLA_DEBUG) {
            var info = '';
            for (var i = 0; i < bitrate.length - 1; ++i) {
                var ui = utility[i];
                var ui1 = utility[i + 1];
                var ri = bitrate[i];
                var ri1 = bitrate[i + 1];
                var th = Vp * ((ui * ri1 - ui1 * ri) / (ri1 - ri) + gp);
                var z = Vp * (ui + gp);
                info += i + ':' + (bitrate[i] / 1000000).toFixed(3) + ' ' + th.toFixed(3) + '/' + z.toFixed(3) + ' ';
            }
            info += ' ' + (bitrate.length - 1) + ':' + (bitrate[bitrate.length - 1] / 1000000).toFixed(3) + ' -/' + (Vp * (utility[bitrate.length - 1] + gp)).toFixed(3);
            log('BolaDebug ' + mediaInfo.type + ' bitrates ' + info);
        }

        return initialState;
    }

    function getQualityFromBufferLevel(bolaState, bufferLevel) {
        var bitrateCount = bolaState.bitrate.length;
        var quality = bitrateCount - 1;
        var score = 0.0;
        for (var i = 0; i < bitrateCount; ++i) {
            var s = (bolaState.utility[i] + bolaState.gp - bufferLevel / bolaState.Vp) / bolaState.bitrate[i];
            if (s > score) {
                score = s;
                quality = i;
            }
        }
        return quality;
    }

    function getLastHttpRequests(metrics, count) {
        var allHttpRequests = dashMetrics.getHttpRequests(metrics);
        var httpRequests = [];
        for (var i = allHttpRequests.length - 1; i >= 0; --i) {
            var request = allHttpRequests[i];
            if (request.type === _voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE && request._tfinish && request.tresponse && request.trace) {
                httpRequests.push(request);
                if (httpRequests.length === count) {
                    break;
                }
            }
        }
        return httpRequests;
    }

    function getLastThroughput(metrics, count, mediaType) {
        // TODO: mediaType only used for debugging, remove it
        // TODO: Should we replace this with an average of the last few throughputs?
        var lastRequests = getLastHttpRequests(metrics, count);
        if (lastRequests.length === 0) {
            return 0.0;
        }

        var totalInverse = 0.0;
        var msg = '';
        for (var i = 0; i < lastRequests.length; ++i) {
            // The RTT delay results in a lower throughput. We can avoid this delay in the calculation, but we do not want to.
            var downloadSeconds = 0.001 * (lastRequests[i]._tfinish.getTime() - lastRequests[i].trequest.getTime());
            var downloadBits = 8 * lastRequests[i].trace.reduce(function (a, b) {
                return a + b.b[0];
            }, 0);
            msg += ' ' + (downloadBits / 1000000).toFixed(3) + '/' + downloadSeconds.toFixed(3) + '=' + (downloadBits / downloadSeconds / 1000000).toFixed(3);
            totalInverse += downloadSeconds / downloadBits;
        }

        if (BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaRule last throughput = ' + (lastRequests.length / totalInverse / 1000000).toFixed(3) + ' :' + msg);

        return lastRequests.length / totalInverse;
    }

    function getQualityFromThroughput(bolaState, throughput) {
        // do not factor in bandwidthSafetyFactor here - it is factored at point of call

        var q = 0;
        for (var i = 1; i < bolaState.bitrate.length; ++i) {
            if (bolaState.bitrate[i] > throughput) {
                break;
            }
            q = i;
        }
        return q;
    }

    function getDelayFromLastFragmentInSeconds(metrics, mediaType) {
        var lastRequests = getLastHttpRequests(metrics, 1);
        if (lastRequests.length === 0) {
            return 0.0;
        }
        var lastRequest = lastRequests[0];
        var nowMilliSeconds = new Date().getTime();
        var lastRequestFinishMilliSeconds = lastRequest._tfinish.getTime();

        if (lastRequestFinishMilliSeconds > nowMilliSeconds) {
            // this shouldn't happen, try to handle gracefully
            lastRequestFinishMilliSeconds = nowMilliSeconds;
        }

        var lct = lastCallTimeDict[mediaType];
        lastCallTimeDict[mediaType] = nowMilliSeconds;
        var delayMilliSeconds = 0.0;
        if (lct && lct > lastRequestFinishMilliSeconds) {
            delayMilliSeconds = nowMilliSeconds - lct;
        } else {
            delayMilliSeconds = nowMilliSeconds - lastRequestFinishMilliSeconds;
        }

        if (delayMilliSeconds < 0.0) return 0.0;
        return 0.001 * delayMilliSeconds;
    }

    function onPlaybackSeeking() {
        // TODO: Verify what happens if we seek mid-fragment.
        // TODO: If we have 10s fragments and seek, we would like to download the first fragment at a lower quality to restart playback quickly.
        for (var i = 0; i < seekMediaTypes.length; ++i) {
            var mediaType = seekMediaTypes[i];
            var metrics = metricsModel.getReadOnlyMetricsFor(mediaType);
            if (metrics.BolaState.length !== 0) {
                var bolaState = metrics.BolaState[0]._s;
                if (bolaState.state !== BOLA_STATE_ONE_BITRATE) {
                    bolaState.state = BOLA_STATE_STARTUP;
                }
                metricsModel.updateBolaState(mediaType, bolaState);
            }
        }
    }

    function onPeriodSwitchStarted() {
        // TODO
    }

    function execute(rulesContext, callback) {
        var streamProcessor = rulesContext.getStreamProcessor();
        streamProcessor.getScheduleController().setTimeToLoadDelay(0.0);

        var switchRequest = (0, _SwitchRequestJs2['default'])(context).create(_SwitchRequestJs2['default'].NO_CHANGE, _SwitchRequestJs2['default'].WEAK);

        var mediaInfo = rulesContext.getMediaInfo();
        var mediaType = mediaInfo.type;
        var metrics = metricsModel.getReadOnlyMetricsFor(mediaType);

        if (metrics.BolaState.length === 0) {
            // initialization

            if (BOLA_DEBUG) log('BolaDebug ' + mediaType + '\nBolaDebug ' + mediaType + ' BolaRule for state=- fragmentStart=' + adapter.getIndexHandlerTime(rulesContext.getStreamProcessor()).toFixed(3));

            var initState = calculateInitialState(rulesContext);
            metricsModel.updateBolaState(mediaType, initState);

            var q = 0;
            if (initState.state !== BOLA_STATE_ONE_BITRATE) {
                // initState.state === BOLA_STATE_STARTUP

                seekMediaTypes.push(mediaType);

                // Bola is not invoked by dash.js to determine the bitrate quality for the first fragment. We might estimate the throughput level here, but the metric related to the HTTP request for the first fragment is usually not available.
                // TODO: at some point, we may want to consider a tweak that redownloads the first fragment at a higher quality

                var initThroughput = getLastThroughput(metrics, initState.throughputCount, mediaType);
                if (initThroughput === 0.0) {
                    // We don't have information about any download yet - let someone else decide quality.
                    if (BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaRule quality unchanged for INITIALIZE');
                    callback(switchRequest);
                    return;
                }
                q = getQualityFromThroughput(initState, initThroughput * initState.bandwidthSafetyFactor);
                initState.lastQuality = q;
                switchRequest = (0, _SwitchRequestJs2['default'])(context).create(q, _SwitchRequestJs2['default'].DEFAULT);
            }

            if (BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaRule quality ' + q + ' for INITIALIZE');
            callback(switchRequest);
            return;
        }

        // metrics.BolaState.length > 0
        var bolaState = metrics.BolaState[0]._s;
        // TODO: does changing bolaState conform to coding style, or should we clone?

        if (bolaState.state === BOLA_STATE_ONE_BITRATE) {
            if (BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaRule quality 0 for ONE_BITRATE');
            callback(switchRequest);
            return;
        }

        if (BOLA_DEBUG) log('BolaDebug ' + mediaType + '\nBolaDebug ' + mediaType + ' EXECUTE BolaRule for state=' + bolaState.state + ' fragmentStart=' + adapter.getIndexHandlerTime(rulesContext.getStreamProcessor()).toFixed(3));

        var bufferLevel = dashMetrics.getCurrentBufferLevel(metrics) ? dashMetrics.getCurrentBufferLevel(metrics) : 0.0;
        var bolaQuality = getQualityFromBufferLevel(bolaState, bufferLevel);
        var lastThroughput = getLastThroughput(metrics, bolaState.throughputCount, mediaType);

        if (BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaRule bufferLevel=' + bufferLevel.toFixed(3) + '(+' + bolaState.virtualBuffer.toFixed(3) + ') lastThroughput=' + (lastThroughput / 1000000.0).toFixed(3) + ' tentativeQuality=' + bolaQuality + ',' + getQualityFromBufferLevel(bolaState, bufferLevel + bolaState.virtualBuffer));

        if (bufferLevel <= 0.1) {
            // rebuffering occurred, reset virtual buffer
            bolaState.virtualBuffer = 0.0;
        }

        if (!bolaState.safetyGuarantee) {
            // we can use virtualBuffer
            // find out if there was delay because of lack of availability or because bolaBufferTarget > bufferTarget
            var timeSinceLastDownload = getDelayFromLastFragmentInSeconds(metrics, mediaType);
            if (timeSinceLastDownload > 0.0) {
                // TODO: maybe we should set some positive threshold here
                bolaState.virtualBuffer += timeSinceLastDownload;
            }
            if (bufferLevel + bolaState.virtualBuffer > bolaState.bolaBufferMax) {
                bolaState.virtualBuffer = bolaState.bolaBufferMax - bufferLevel;
            }
            if (bolaState.virtualBuffer < 0.0) {
                bolaState.virtualBuffer = 0.0;
            }

            // update bolaQuality using virtualBuffer: bufferLevel might be artificially low because of lack of availability

            var bolaQualityVirtual = getQualityFromBufferLevel(bolaState, bufferLevel + bolaState.virtualBuffer);
            if (bolaQualityVirtual > bolaQuality) {
                // May use quality higher than that indicated by real buffer level.

                // In this case, make sure there is enough throughput to download a fragment before real buffer runs out.

                var maxQuality = bolaQuality;
                while (maxQuality < bolaQualityVirtual && bolaState.bitrate[maxQuality + 1] * bolaState.fragmentDuration / (lastThroughput * bolaState.bandwidthSafetyFactor) < bufferLevel) {
                    ++maxQuality;
                }

                // TODO: maybe we can use a more conservative level here, but this should be OK

                if (maxQuality > bolaQuality) {
                    // We can (and will) download at a quality higher than that indicated by real buffer level.
                    if (bolaQualityVirtual <= maxQuality) {
                        // we can download fragment indicated by real+virtual buffer without rebuffering
                        bolaQuality = bolaQualityVirtual;
                    } else {
                        // downloading fragment indicated by real+virtual rebuffers, use lower quality
                        bolaQuality = maxQuality;
                        // deflate virtual buffer to match quality
                        // TODO: document the math
                        var targetBufferLevel = bolaState.Vp * (bolaState.gp + bolaState.utility[bolaQuality]);
                        if (bufferLevel + bolaState.virtualBuffer > targetBufferLevel) {
                            bolaState.virtualBuffer = targetBufferLevel - bufferLevel;
                            if (bolaState.virtualBuffer < 0.0) {
                                // should be false
                                bolaState.virtualBuffer = 0.0;
                            }
                        }
                    }
                }
            }
        } // !bolaState.safetyGuarantee: we can use virtualBuffer

        if (bolaState.state === BOLA_STATE_STARTUP || bolaState.state === BOLA_STATE_STARTUP_NO_INC) {
            // in startup phase, use some throughput estimation

            var q = getQualityFromThroughput(bolaState, lastThroughput * bolaState.bandwidthSafetyFactor);

            if (lastThroughput <= 0.0) {
                // something went wrong - go to steady state
                bolaState.state = BOLA_STATE_STEADY;
            }
            if (bolaState.state === BOLA_STATE_STARTUP && q < bolaState.lastQuality) {
                // Since the quality is decreasing during startup, it will not be allowed to increase again.
                bolaState.state = BOLA_STATE_STARTUP_NO_INC;
            }
            if (bolaState.state === BOLA_STATE_STARTUP_NO_INC && q > bolaState.lastQuality) {
                // In this state the quality is not allowed to increase until steady state.
                q = bolaState.lastQuality;
            }
            if (q <= bolaQuality) {
                // Since the buffer is full enough for steady state operation to match startup operation, switch over to steady state.
                bolaState.state = BOLA_STATE_STEADY;
            }
            if (bolaState.state !== BOLA_STATE_STEADY) {
                // still in startup mode
                if (BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaRule quality ' + q + '>' + bolaQuality + ' for STARTUP');
                bolaState.lastQuality = q;
                metricsModel.updateBolaState(mediaType, bolaState);
                switchRequest = (0, _SwitchRequestJs2['default'])(context).create(q, _SwitchRequestJs2['default'].DEFAULT);
                callback(switchRequest);
                return;
            }
        }

        // steady state

        // we want to avoid oscillations
        // We implement the "BOLA-O" variant: when network bandwidth lies between two encoded bitrate levels, stick to the lowest level.
        var delaySeconds = 0.0;
        if (bolaQuality > bolaState.lastQuality) {
            // do not multiply throughput by bandwidthSafetyFactor here: we are not using throughput estimation but capping bitrate to avoid oscillations
            var q = getQualityFromThroughput(bolaState, lastThroughput);
            if (bolaQuality > q) {
                // only intervene if we are trying to *increase* quality to an *unsustainable* level

                if (q < bolaState.lastQuality) {
                    // we are only avoid oscillations - do not drop below last quality
                    q = bolaState.lastQuality;
                } else {
                    // We are dropping to an encoded bitrate which is a little less than the network bandwidth because bitrate levels are discrete. Quality q might lead to buffer inflation, so we deflate buffer to the level that q gives postive utility.
                    var wantBufferLevel = bolaState.Vp * (bolaState.utility[q] + bolaState.gp);
                    delaySeconds = bufferLevel - wantBufferLevel;
                }
                bolaQuality = q;
            }
        }

        if (delaySeconds > 0.0) {
            // first reduce virtual buffer
            if (delaySeconds > bolaState.virtualBuffer) {
                delaySeconds -= bolaState.virtualBuffer;
                bolaState.virtualBuffer = 0.0;
            } else {
                bolaState.virtualBuffer -= delaySeconds;
                delaySeconds = 0.0;
            }
        }
        if (delaySeconds > 0.0) {
            streamProcessor.getScheduleController().setTimeToLoadDelay(1000.0 * delaySeconds);
        }

        bolaState.lastQuality = bolaQuality;
        metricsModel.updateBolaState(mediaType, bolaState);
        switchRequest = (0, _SwitchRequestJs2['default'])(context).create(bolaQuality, _SwitchRequestJs2['default'].DEFAULT);
        if (BOLA_DEBUG) log('BolaDebug ' + mediaType + ' BolaRule quality ' + bolaQuality + ' delay=' + delaySeconds.toFixed(3) + ' for STEADY');
        callback(switchRequest);
    }

    function reset() {
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, instance);
        eventBus.off(_coreEventsEventsJs2['default'].PERIOD_SWITCH_STARTED, onPeriodSwitchStarted, instance);
        setup();
    }

    instance = {
        execute: execute,
        reset: reset
    };

    setup();
    return instance;
}

BolaRule.__dashjs_factory_name = 'BolaRule';
var factory = _coreFactoryMakerJs2['default'].getClassFactory(BolaRule);
factory.BOLA_STATE_ONE_BITRATE = BOLA_STATE_ONE_BITRATE;
factory.BOLA_STATE_STARTUP = BOLA_STATE_STARTUP;
factory.BOLA_STATE_STARTUP_NO_INC = BOLA_STATE_STARTUP_NO_INC;
factory.BOLA_STATE_STEADY = BOLA_STATE_STEADY;
factory.BOLA_DEBUG = BOLA_DEBUG; // TODO: remove
exports['default'] = factory;
module.exports = exports['default'];

},{"../../../core/Debug.js":5,"../../../core/EventBus.js":6,"../../../core/FactoryMaker.js":7,"../../../core/events/Events.js":9,"../../../dash/DashAdapter.js":11,"../../controllers/PlaybackController.js":54,"../../models/MediaPlayerModel.js":64,"../../vo/metrics/HTTPRequest.js":117,"../SwitchRequest.js":70}],75:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _SwitchRequestJs = _dereq_('../SwitchRequest.js');

var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs);

var _modelsMediaPlayerModelJs = _dereq_('../../models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

var _controllersAbrControllerJs = _dereq_('../../controllers/AbrController.js');

var _controllersAbrControllerJs2 = _interopRequireDefault(_controllersAbrControllerJs);

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

function BufferOccupancyRule(config) {

    var instance = undefined;
    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;

    var metricsModel = config.metricsModel;
    var dashMetrics = config.dashMetrics;

    var lastSwitchTime = undefined,
        mediaPlayerModel = undefined;

    function setup() {
        lastSwitchTime = 0;
        mediaPlayerModel = (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance();
    }

    function execute(rulesContext, callback) {
        var now = new Date().getTime() / 1000;
        var mediaInfo = rulesContext.getMediaInfo();
        var representationInfo = rulesContext.getTrackInfo();
        var mediaType = mediaInfo.type;
        var waitToSwitchTime = !isNaN(representationInfo.fragmentDuration) ? representationInfo.fragmentDuration / 2 : 2;
        var current = rulesContext.getCurrentValue();
        var streamProcessor = rulesContext.getStreamProcessor();
        var abrController = streamProcessor.getABRController();
        var metrics = metricsModel.getReadOnlyMetricsFor(mediaType);
        var lastBufferLevel = dashMetrics.getCurrentBufferLevel(metrics);
        var lastBufferStateVO = metrics.BufferState.length > 0 ? metrics.BufferState[metrics.BufferState.length - 1] : null;
        var isBufferRich = false;
        var maxIndex = mediaInfo.representationCount - 1;
        var switchRequest = (0, _SwitchRequestJs2['default'])(context).create(_SwitchRequestJs2['default'].NO_CHANGE, _SwitchRequestJs2['default'].WEAK);

        if (now - lastSwitchTime < waitToSwitchTime || abrController.getAbandonmentStateFor(mediaType) === _controllersAbrControllerJs2['default'].ABANDON_LOAD) {
            callback(switchRequest);
            return;
        }

        if (lastBufferStateVO !== null) {
            // This will happen when another rule tries to switch from top to any other.
            // If there is enough buffer why not try to stay at high level.
            if (lastBufferLevel > lastBufferStateVO.target) {
                isBufferRich = lastBufferLevel - lastBufferStateVO.target > mediaPlayerModel.getRichBufferThreshold();

                if (isBufferRich && mediaInfo.representationCount > 1) {
                    switchRequest = (0, _SwitchRequestJs2['default'])(context).create(maxIndex, _SwitchRequestJs2['default'].STRONG);
                }
            }
        }

        if (switchRequest.value !== _SwitchRequestJs2['default'].NO_CHANGE && switchRequest.value !== current) {
            log('BufferOccupancyRule requesting switch to index: ', switchRequest.value, 'type: ', mediaType, ' Priority: ', switchRequest.priority === _SwitchRequestJs2['default'].DEFAULT ? 'Default' : switchRequest.priority === _SwitchRequestJs2['default'].STRONG ? 'Strong' : 'Weak');
        }

        callback(switchRequest);
    }

    function reset() {
        lastSwitchTime = 0;
    }

    instance = {
        execute: execute,
        reset: reset
    };

    setup();

    return instance;
}

BufferOccupancyRule.__dashjs_factory_name = 'BufferOccupancyRule';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(BufferOccupancyRule);
module.exports = exports['default'];

},{"../../../core/Debug.js":5,"../../../core/FactoryMaker.js":7,"../../controllers/AbrController.js":46,"../../models/MediaPlayerModel.js":64,"../SwitchRequest.js":70}],76:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _SwitchRequestJs = _dereq_('../SwitchRequest.js');

var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs);

var _controllersBufferControllerJs = _dereq_('../../controllers/BufferController.js');

var _controllersBufferControllerJs2 = _interopRequireDefault(_controllersBufferControllerJs);

var _coreEventBusJs = _dereq_('../../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

function InsufficientBufferRule(config) {

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var metricsModel = config.metricsModel;

    var instance = undefined,
        bufferStateDict = undefined,
        lastSwitchTime = undefined,
        waitToSwitchTime = undefined;

    function setup() {
        bufferStateDict = {};
        lastSwitchTime = 0;
        waitToSwitchTime = 1000;
        eventBus.on(_coreEventsEventsJs2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, instance);
    }

    function execute(rulesContext, callback) {
        var now = new Date().getTime();
        var mediaType = rulesContext.getMediaInfo().type;
        var current = rulesContext.getCurrentValue();
        var metrics = metricsModel.getReadOnlyMetricsFor(mediaType);
        var lastBufferStateVO = metrics.BufferState.length > 0 ? metrics.BufferState[metrics.BufferState.length - 1] : null;
        var switchRequest = (0, _SwitchRequestJs2['default'])(context).create(_SwitchRequestJs2['default'].NO_CHANGE, _SwitchRequestJs2['default'].WEAK);

        if (now - lastSwitchTime < waitToSwitchTime || lastBufferStateVO === null) {
            callback(switchRequest);
            return;
        }

        setBufferInfo(mediaType, lastBufferStateVO.state);
        // After the sessions first buffer loaded event , if we ever have a buffer empty event we want to switch all the way down.
        if (lastBufferStateVO.state === _controllersBufferControllerJs2['default'].BUFFER_EMPTY && bufferStateDict[mediaType].firstBufferLoadedEvent !== undefined) {
            switchRequest = (0, _SwitchRequestJs2['default'])(context).create(0, _SwitchRequestJs2['default'].STRONG);
        }

        if (switchRequest.value !== _SwitchRequestJs2['default'].NO_CHANGE && switchRequest.value !== current) {
            log('InsufficientBufferRule requesting switch to index: ', switchRequest.value, 'type: ', mediaType, ' Priority: ', switchRequest.priority === _SwitchRequestJs2['default'].DEFAULT ? 'Default' : switchRequest.priority === _SwitchRequestJs2['default'].STRONG ? 'Strong' : 'Weak');
        }

        lastSwitchTime = now;
        callback(switchRequest);
    }

    function setBufferInfo(type, state) {
        bufferStateDict[type] = bufferStateDict[type] || {};
        bufferStateDict[type].state = state;
        if (state === _controllersBufferControllerJs2['default'].BUFFER_LOADED && !bufferStateDict[type].firstBufferLoadedEvent) {
            bufferStateDict[type].firstBufferLoadedEvent = true;
        }
    }

    function onPlaybackSeeking() {
        bufferStateDict = {};
    }

    function reset() {
        eventBus.off(_coreEventsEventsJs2['default'].PLAYBACK_SEEKING, onPlaybackSeeking, instance);
        bufferStateDict = {};
        lastSwitchTime = 0;
    }

    instance = {
        execute: execute,
        reset: reset
    };

    setup();

    return instance;
}

InsufficientBufferRule.__dashjs_factory_name = 'InsufficientBufferRule';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(InsufficientBufferRule);
module.exports = exports['default'];

},{"../../../core/Debug.js":5,"../../../core/EventBus.js":6,"../../../core/FactoryMaker.js":7,"../../../core/events/Events.js":9,"../../controllers/BufferController.js":49,"../SwitchRequest.js":70}],77:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _SwitchRequestJs = _dereq_('../SwitchRequest.js');

var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs);

var _controllersBufferControllerJs = _dereq_('../../controllers/BufferController.js');

var _controllersBufferControllerJs2 = _interopRequireDefault(_controllersBufferControllerJs);

var _controllersAbrControllerJs = _dereq_('../../controllers/AbrController.js');

var _controllersAbrControllerJs2 = _interopRequireDefault(_controllersAbrControllerJs);

var _modelsMediaPlayerModelJs = _dereq_('../../models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

var _voMetricsHTTPRequestJs = _dereq_('../../vo/metrics/HTTPRequest.js');

var _voMetricsHTTPRequestJs2 = _interopRequireDefault(_voMetricsHTTPRequestJs);

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

function ThroughputRule(config) {

    var AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_LIVE = 2;
    var AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_VOD = 3;

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var dashMetrics = config.dashMetrics;
    var metricsModel = config.metricsModel;
    var isWebOS = !!window.PalmSystem;

    var instance = undefined,
        throughputArray = undefined,
        videoLimit = undefined,
        mediaPlayerModel = undefined;

    function setup() {
        throughputArray = [];
        mediaPlayerModel = (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance();
        if (isWebOS) {
            var devInfo = JSON.parse(window.PalmSystem.deviceInfo);
            if (devInfo.screenWidth < 1920) {
                videoLimit = { width: 1280, height: 720 };
            } else if (devInfo.screenWidth < 3840) {
                videoLimit = { width: 1920, height: 1080 };
            } else {
                videoLimit = { width: 3840, height: 2160 };
            }
        }
    }

    function storeLastRequestThroughputByType(type, lastRequestThroughput) {
        throughputArray[type] = throughputArray[type] || [];
        if (lastRequestThroughput !== Infinity && lastRequestThroughput !== throughputArray[type][throughputArray[type].length - 1]) {
            throughputArray[type].push(lastRequestThroughput);
        }
    }

    function getAverageThroughput(type, isDynamic) {
        var averageThroughput = 0;
        var sampleAmount = isDynamic ? AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_LIVE : AVERAGE_THROUGHPUT_SAMPLE_AMOUNT_VOD;
        var arr = throughputArray[type];
        var len = arr.length;

        sampleAmount = len < sampleAmount ? len : sampleAmount;

        if (len > 0) {
            var startValue = len - sampleAmount;
            var totalSampledValue = 0;

            for (var i = startValue; i < len; i++) {
                totalSampledValue += arr[i];
            }
            averageThroughput = totalSampledValue / sampleAmount;
        }

        if (arr.length > sampleAmount) {
            arr.shift();
        }

        return averageThroughput / 1000 * mediaPlayerModel.getBandwidthSafetyFactor();
    }

    function execute(rulesContext, callback) {
        var downloadTime;
        var bytes;
        var averageThroughput;
        var lastRequestThroughput;

        var mediaInfo = rulesContext.getMediaInfo();
        var mediaType = mediaInfo.type;
        var current = rulesContext.getCurrentValue();
        var metrics = metricsModel.getReadOnlyMetricsFor(mediaType);
        var streamProcessor = rulesContext.getStreamProcessor();
        var abrController = streamProcessor.getABRController();
        var isDynamic = streamProcessor.isDynamic();
        var lastRequest = dashMetrics.getCurrentHttpRequest(metrics);
        var bufferStateVO = metrics.BufferState.length > 0 ? metrics.BufferState[metrics.BufferState.length - 1] : null;
        var bufferLevelVO = metrics.BufferLevel.length > 0 ? metrics.BufferLevel[metrics.BufferLevel.length - 1] : null;
        var switchRequest = (0, _SwitchRequestJs2['default'])(context).create(_SwitchRequestJs2['default'].NO_CHANGE, _SwitchRequestJs2['default'].WEAK);

        if (!metrics || !lastRequest || lastRequest.type !== _voMetricsHTTPRequestJs2['default'].MEDIA_SEGMENT_TYPE || !bufferStateVO || !bufferLevelVO) {

            callback(switchRequest);
            return;
        }

        if (lastRequest.trace && lastRequest.trace.length) {
            downloadTime = (lastRequest._tfinish.getTime() - lastRequest.tresponse.getTime()) / 1000;

            bytes = lastRequest.trace.reduce(function (a, b) {
                return a + b.b[0];
            }, 0);

            lastRequestThroughput = Math.round(bytes * 8) / downloadTime;
            storeLastRequestThroughputByType(mediaType, lastRequestThroughput);
        }

        averageThroughput = Math.round(getAverageThroughput(mediaType, isDynamic));
        abrController.setAverageThroughput(mediaType, averageThroughput);

        if (abrController.getAbandonmentStateFor(mediaType) !== _controllersAbrControllerJs2['default'].ABANDON_LOAD) {

            if (bufferStateVO.state === _controllersBufferControllerJs2['default'].BUFFER_LOADED || isDynamic) {
                var newQuality = abrController.getQualityForBitrate(mediaInfo, averageThroughput);
                if (mediaType === 'video' && isWebOS) {
                    var bitrateList = abrController.getBitrateList(mediaInfo);
                    while (newQuality && (bitrateList[newQuality].height > videoLimit.height || bitrateList[newQuality].width > videoLimit.width)) {
                        newQuality--;
                    }
                }
                streamProcessor.getScheduleController().setTimeToLoadDelay(0); // TODO Watch out for seek event - no delay when seeking.!!
                switchRequest = (0, _SwitchRequestJs2['default'])(context).create(newQuality, _SwitchRequestJs2['default'].DEFAULT);
            }

            if (switchRequest.value !== _SwitchRequestJs2['default'].NO_CHANGE && switchRequest.value !== current) {
                log('ThroughputRule requesting switch to index: ', switchRequest.value, 'type: ', mediaType, ' Priority: ', switchRequest.priority === _SwitchRequestJs2['default'].DEFAULT ? 'Default' : switchRequest.priority === _SwitchRequestJs2['default'].STRONG ? 'Strong' : 'Weak', 'Average throughput', Math.round(averageThroughput), 'kbps');
            }
        }

        callback(switchRequest);
    }

    function reset() {
        setup();
    }

    instance = {
        execute: execute,
        reset: reset
    };

    setup();
    return instance;
}

ThroughputRule.__dashjs_factory_name = 'ThroughputRule';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(ThroughputRule);
module.exports = exports['default'];

},{"../../../core/Debug.js":5,"../../../core/FactoryMaker.js":7,"../../controllers/AbrController.js":46,"../../controllers/BufferController.js":49,"../../models/MediaPlayerModel.js":64,"../../vo/metrics/HTTPRequest.js":117,"../SwitchRequest.js":70}],78:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function BasicSelector(config) {

    var instance = undefined;

    var blacklistController = config.blacklistController;

    function select(baseUrls) {
        var index = 0;
        var selectedBaseUrl;

        if (baseUrls && baseUrls.some(function (baseUrl, idx) {
            index = idx;

            return !blacklistController.contains(baseUrl.serviceLocation);
        })) {
            selectedBaseUrl = baseUrls[index];
        }

        return selectedBaseUrl;
    }

    instance = {
        select: select
    };

    return instance;
}

BasicSelector.__dashjs_factory_name = 'BasicSelector';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(BasicSelector);
module.exports = exports['default'];

},{"../../../core/FactoryMaker.js":7}],79:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function DVBSelector(config) {

    var instance = undefined;

    var blacklistController = config.blacklistController;

    function getNonBlacklistedBaseUrls(urls) {
        var removedPriorities = [];

        var samePrioritiesFilter = function samePrioritiesFilter(el) {
            if (removedPriorities.length) {
                if (el.dvb_priority && removedPriorities.indexOf(el.dvb_priority) !== -1) {
                    return false;
                }
            }

            return true;
        };

        var serviceLocationFilter = function serviceLocationFilter(baseUrl) {
            if (blacklistController.contains(baseUrl.serviceLocation)) {
                // whenever a BaseURL is removed from the available list of
                // BaseURLs, any other BaseURL with the same @priority
                // value as the BaseURL being removed shall also be removed
                if (baseUrl.dvb_priority) {
                    removedPriorities.push(baseUrl.dvb_priority);
                }

                // all URLs in the list which have a @serviceLocation
                // attribute matching an entry in the blacklist shall be
                // removed from the available list of BaseURLs
                return false;
            }

            return true;
        };

        return urls.filter(serviceLocationFilter).filter(samePrioritiesFilter);
    }

    function selectByWeight(availableUrls) {
        var prioritySorter = function prioritySorter(a, b) {
            var diff = a.dvb_priority - b.dvb_priority;
            return isNaN(diff) ? 0 : diff;
        };

        var topPriorityFilter = function topPriorityFilter(baseUrl, idx, arr) {
            return !idx || arr[0].dvb_priority && baseUrl.dvb_priority && arr[0].dvb_priority === baseUrl.dvb_priority;
        };

        var totalWeight = 0;
        var cumulWeights = [];
        var idx = 0;
        var rn;
        var urls;

        // It shall begin by taking the set of resolved BaseURLs present or inherited at the current
        // position in the MPD, resolved and filtered as described in 10.8.2.1, that have the lowest
        // @priority attribute value.
        urls = availableUrls.sort(prioritySorter).filter(topPriorityFilter);

        if (urls.length) {
            if (urls.length > 1) {
                // If there is more than one BaseURL with this lowest @priority attribute value then the Player
                // shall select one of them at random such that the probability of each BaseURL being chosen
                // is proportional to the value of its @weight attribute. The method described in RFC 2782
                // [26] or picking from a number of weighted entries is suitable for this, but there may be other
                // algorithms which achieve the same effect.

                // add all the weights together, storing the accumulated weight per entry
                urls.forEach(function (baseUrl) {
                    totalWeight += baseUrl.dvb_weight;
                    cumulWeights.push(totalWeight);
                });

                // pick a random number between zero and totalWeight
                rn = Math.floor(Math.random() * (totalWeight - 1));

                // select the index for the range rn falls within
                cumulWeights.every(function (limit, index) {
                    idx = index;

                    if (rn < limit) {
                        return false;
                    }

                    return true;
                });
            }

            return urls[idx];
        }
    }

    function select(baseUrls) {
        return baseUrls && selectByWeight(getNonBlacklistedBaseUrls(baseUrls));
    }

    instance = {
        select: select
    };

    return instance;
}

DVBSelector.__dashjs_factory_name = 'DVBSelector';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(DVBSelector);
module.exports = exports['default'];

},{"../../../core/FactoryMaker.js":7}],80:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _modelsMediaPlayerModelJs = _dereq_('../../models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

var _controllersPlaybackControllerJs = _dereq_('../../controllers/PlaybackController.js');

var _controllersPlaybackControllerJs2 = _interopRequireDefault(_controllersPlaybackControllerJs);

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function BufferLevelRule(config) {

    var instance = undefined;
    var context = this.context;

    var dashMetrics = config.dashMetrics;
    var metricsModel = config.metricsModel;
    var textSourceBuffer = config.textSourceBuffer;

    var mediaPlayerModel = undefined,
        playbackController = undefined;

    function setup() {
        mediaPlayerModel = (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance();
        playbackController = (0, _controllersPlaybackControllerJs2['default'])(context).getInstance();
    }

    function execute(streamProcessor) {

        var representationInfo = streamProcessor.getCurrentRepresentationInfo();
        var mediaInfo = representationInfo.mediaInfo;
        var mediaType = mediaInfo.type;
        var metrics = metricsModel.getReadOnlyMetricsFor(mediaType);
        var bufferLevel = dashMetrics.getCurrentBufferLevel(metrics);

        return bufferLevel < getBufferTarget(streamProcessor, mediaType);
    }

    function reset() {}

    function getBufferTarget(streamProcessor, type) {

        var representationInfo = streamProcessor.getCurrentRepresentationInfo();
        var mediaInfo = representationInfo.mediaInfo;
        var streamInfo = mediaInfo.streamInfo;
        var abrController = streamProcessor.getABRController();
        var duration = streamInfo.manifestInfo.duration;
        var isLongFormContent = duration >= mediaPlayerModel.getLongFormContentDurationThreshold();
        var bufferTarget = NaN;

        if (type === 'fragmentedText') {
            bufferTarget = textSourceBuffer.getAllTracksAreDisabled() ? 0 : representationInfo.fragmentDuration;
        } else {
            if (abrController.isPlayingAtTopQuality(streamInfo)) {
                bufferTarget = isLongFormContent ? mediaPlayerModel.getBufferTimeAtTopQualityLongForm() : mediaPlayerModel.getBufferTimeAtTopQuality();
            } else {
                bufferTarget = mediaPlayerModel.getStableBufferTime();
            }
        }
        return bufferTarget;
    }

    instance = {
        execute: execute,
        reset: reset
    };
    setup();
    return instance;
}

BufferLevelRule.__dashjs_factory_name = 'BufferLevelRule';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(BufferLevelRule);
module.exports = exports['default'];

},{"../../../core/FactoryMaker.js":7,"../../controllers/PlaybackController.js":54,"../../models/MediaPlayerModel.js":64}],81:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreDebugJs = _dereq_('../../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function NextFragmentRequestRule(config) {

    var instance = undefined;
    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;
    var adapter = config.adapter;
    var sourceBufferController = config.sourceBufferController;
    var virtualBuffer = config.virtualBuffer;
    var textSourceBuffer = config.textSourceBuffer;

    function execute(streamProcessor) {

        var representationInfo = streamProcessor.getCurrentRepresentationInfo();
        var mediaInfo = representationInfo.mediaInfo;
        var mediaType = mediaInfo.type;
        var streamId = mediaInfo.streamInfo.id;
        var scheduleController = streamProcessor.getScheduleController();
        var seekTarget = scheduleController.getSeekTarget();
        var hasSeekTarget = !isNaN(seekTarget);
        var keepIdx = !hasSeekTarget;
        var time = hasSeekTarget ? seekTarget : adapter.getIndexHandlerTime(streamProcessor);
        var buffer = streamProcessor.getBuffer();
        var range = null;
        var appendedChunks = undefined;
        var request = undefined;

        if (isNaN(time) || mediaType === 'fragmentedText' && textSourceBuffer.getAllTracksAreDisabled()) {
            return null;
        }

        if (hasSeekTarget) {
            scheduleController.setSeekTarget(NaN);
        }

        /**
         * This is critical for IE/Safari/EDGE
         * */
        if (buffer) {
            range = sourceBufferController.getBufferRange(streamProcessor.getBuffer(), time);
            if (range !== null) {
                appendedChunks = virtualBuffer.getChunks({ streamId: streamId, mediaType: mediaType, appended: true, mediaInfo: mediaInfo, forRange: range });
                if (appendedChunks && appendedChunks.length > 0) {
                    var t = time;
                    time = appendedChunks[appendedChunks.length - 1].bufferedRange.end;
                    log('Prior to making a request for time, NextFragmentRequestRule is aligning index handler\'s currentTime with bufferedRange.end.', t, ' was changed to ', time);
                }
            }
        }

        request = adapter.getFragmentRequestForTime(streamProcessor, representationInfo, time, { keepIdx: keepIdx });
        //log("getForTime", request, time);
        if (request && streamProcessor.getFragmentModel().isFragmentLoaded(request)) {
            request = adapter.getNextFragmentRequest(streamProcessor, representationInfo);
            //log("getForNext", request, streamProcessor.getIndexHandler().getCurrentIndex());
        }

        if (request) {
            adapter.setIndexHandlerTime(streamProcessor, request.startTime + request.duration);
            request.delayLoadingTime = new Date().getTime() + scheduleController.getTimeToLoadDelay();
            scheduleController.setTimeToLoadDelay(0); // only delay one fragment
        }

        return request;
    }

    instance = {
        execute: execute
    };

    return instance;
}

NextFragmentRequestRule.__dashjs_factory_name = 'NextFragmentRequestRule';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(NextFragmentRequestRule);
module.exports = exports['default'];

},{"../../../core/Debug.js":5,"../../../core/FactoryMaker.js":7}],82:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _SwitchRequestJs = _dereq_('../SwitchRequest.js');

var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs);

var _coreEventBusJs = _dereq_('../../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var SEARCH_TIME_SPAN = 12 * 60 * 60; // set the time span that limits our search range to a 12 hours in seconds

function LiveEdgeBinarySearchRule(config) {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var adapter = config.adapter;
    var timelineConverter = config.timelineConverter;

    var instance = undefined,
        liveEdgeInitialSearchPosition = undefined,
        liveEdgeSearchRange = undefined,
        liveEdgeSearchStep = undefined,
        representationInfo = undefined,
        useBinarySearch = undefined,
        fragmentDuration = undefined,
        p = undefined,
        callback = undefined,
        fragmentLoader = undefined,
        streamProcessor = undefined;

    function setup() {
        liveEdgeInitialSearchPosition = NaN;
        liveEdgeSearchRange = null;
        liveEdgeSearchStep = NaN;
        representationInfo = null;
        useBinarySearch = false;
        fragmentDuration = NaN;
        p = _SwitchRequestJs2['default'].DEFAULT;
    }

    function execute(rulesContext, callbackFunc) {
        var request, DVRWindow; // all fragments are supposed to be available in this interval

        callback = callbackFunc;
        streamProcessor = rulesContext.getStreamProcessor();
        fragmentLoader = streamProcessor.getFragmentLoader();
        representationInfo = rulesContext.getTrackInfo();
        fragmentDuration = representationInfo.fragmentDuration;
        DVRWindow = representationInfo.DVRWindow; // all fragments are supposed to be available in this interval

        // start position of the search, it is supposed to be a live edge - the last available fragment for the current mpd
        liveEdgeInitialSearchPosition = DVRWindow.end;

        if (representationInfo.useCalculatedLiveEdgeTime) {
            //By default an expected live edge is the end of the last segment.
            // A calculated live edge ('end' property of a range returned by TimelineConverter.calcSegmentAvailabilityRange)
            // is used as an initial point for finding the actual live edge.
            // But for SegmentTimeline mpds (w/o a negative @r) the end of the
            // last segment is the actual live edge. At the same time, calculated live edge is an expected live edge.
            // Thus, we need to switch an expected live edge and actual live edge for SegmentTimeline streams.
            var actualLiveEdge = timelineConverter.getExpectedLiveEdge();
            timelineConverter.setExpectedLiveEdge(liveEdgeInitialSearchPosition);
            callback((0, _SwitchRequestJs2['default'])(context).create(actualLiveEdge, p));
            return;
        }

        // we should search for a live edge in a time range which is limited by SEARCH_TIME_SPAN.
        liveEdgeSearchRange = { start: Math.max(0, liveEdgeInitialSearchPosition - SEARCH_TIME_SPAN), end: liveEdgeInitialSearchPosition + SEARCH_TIME_SPAN };
        // we have to use half of the availability interval (window) as a search step to ensure that we find a fragment in the window
        liveEdgeSearchStep = Math.floor((DVRWindow.end - DVRWindow.start) / 2);
        // start search from finding a request for the initial search time
        request = adapter.getFragmentRequestForTime(streamProcessor, representationInfo, liveEdgeInitialSearchPosition, { ignoreIsFinished: true });
        findLiveEdge(liveEdgeInitialSearchPosition, onSearchForFragmentSucceeded, onSearchForFragmentFailed, request);
    }

    function reset() {
        liveEdgeInitialSearchPosition = NaN;
        liveEdgeSearchRange = null;
        liveEdgeSearchStep = NaN;
        representationInfo = null;
        useBinarySearch = false;
        fragmentDuration = NaN;
        streamProcessor = null;
        fragmentLoader = null;
    }

    function findLiveEdge(searchTime, onSuccess, onError, request) {
        var req;
        if (request === null) {
            // request can be null because it is out of the generated list of request. In this case we need to
            // update the list and the DVRWindow
            // try to get request object again
            req = adapter.generateFragmentRequestForTime(streamProcessor, representationInfo, searchTime);
            findLiveEdge(searchTime, onSuccess, onError, req);
        } else {
            var handler = function handler(e) {
                eventBus.off(_coreEventsEventsJs2['default'].CHECK_FOR_EXISTENCE_COMPLETED, handler, this);
                if (e.exists) {
                    onSuccess(e.request, searchTime);
                } else {
                    onError(e.request, searchTime);
                }
            };

            eventBus.on(_coreEventsEventsJs2['default'].CHECK_FOR_EXISTENCE_COMPLETED, handler, this);
            fragmentLoader.checkForExistence(request);
        }
    }

    function onSearchForFragmentFailed(request, lastSearchTime) {
        var searchTime, req, searchInterval;

        if (useBinarySearch) {
            binarySearch(false, lastSearchTime);
            return;
        }

        // we have not found any available fragments yet, update the search interval
        searchInterval = lastSearchTime - liveEdgeInitialSearchPosition;
        // we search forward and backward from the start position, increasing the search interval by the value of the half of the availability interval - liveEdgeSearchStep
        searchTime = searchInterval > 0 ? liveEdgeInitialSearchPosition - searchInterval : liveEdgeInitialSearchPosition + Math.abs(searchInterval) + liveEdgeSearchStep;

        // if the search time is out of the range bounds we have not be able to find live edge, stop trying
        if (searchTime < liveEdgeSearchRange.start && searchTime > liveEdgeSearchRange.end) {
            callback((0, _SwitchRequestJs2['default'])(context).create(null, p));
        } else {
            // continue searching for a first available fragment
            req = adapter.getFragmentRequestForTime(streamProcessor, representationInfo, searchTime, { ignoreIsFinished: true });
            findLiveEdge(searchTime, onSearchForFragmentSucceeded, onSearchForFragmentFailed, req);
        }
    }

    function onSearchForFragmentSucceeded(request, lastSearchTime) {
        var startTime = request.startTime;
        var req, searchTime;

        if (!useBinarySearch) {
            // if the fragment duration is unknown we cannot use binary search because we will not be able to
            // decide when to stop the search, so let the start time of the current fragment be a liveEdge
            if (!representationInfo.fragmentDuration) {
                callback((0, _SwitchRequestJs2['default'])(context).create(startTime, p));
                return;
            }
            useBinarySearch = true;
            liveEdgeSearchRange.end = startTime + 2 * liveEdgeSearchStep;

            //if the first request has succeeded we should check next fragment - if it does not exist we have found live edge,
            // otherwise start binary search to find live edge
            if (lastSearchTime === liveEdgeInitialSearchPosition) {
                searchTime = lastSearchTime + fragmentDuration;
                req = adapter.getFragmentRequestForTime(streamProcessor, representationInfo, searchTime, { ignoreIsFinished: true });
                findLiveEdge(searchTime, function () {
                    binarySearch(true, searchTime);
                }, function () {
                    callback((0, _SwitchRequestJs2['default'])(context).create(searchTime, p));
                }, req);

                return;
            }
        }

        binarySearch(true, lastSearchTime);
    }

    function binarySearch(lastSearchSucceeded, lastSearchTime) {
        var isSearchCompleted, req, searchTime;

        if (lastSearchSucceeded) {
            liveEdgeSearchRange.start = lastSearchTime;
        } else {
            liveEdgeSearchRange.end = lastSearchTime;
        }

        isSearchCompleted = Math.floor(liveEdgeSearchRange.end - liveEdgeSearchRange.start) <= fragmentDuration;

        if (isSearchCompleted) {
            // search completed, we should take the time of the last found fragment. If the last search succeeded we
            // take this time. Otherwise, we should subtract the time of the search step which is equal to fragment duration
            callback((0, _SwitchRequestJs2['default'])(context).create(lastSearchSucceeded ? lastSearchTime : lastSearchTime - fragmentDuration, p));
        } else {
            // update the search time and continue searching
            searchTime = (liveEdgeSearchRange.start + liveEdgeSearchRange.end) / 2;
            req = adapter.getFragmentRequestForTime(streamProcessor, representationInfo, searchTime, { ignoreIsFinished: true });
            findLiveEdge(searchTime, onSearchForFragmentSucceeded, onSearchForFragmentFailed, req);
        }
    }

    instance = {
        execute: execute,
        reset: reset
    };

    setup();

    return instance;
}

LiveEdgeBinarySearchRule.__dashjs_factory_name = 'LiveEdgeBinarySearchRule';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(LiveEdgeBinarySearchRule);
module.exports = exports['default'];

},{"../../../core/EventBus.js":6,"../../../core/FactoryMaker.js":7,"../../../core/events/Events.js":9,"../SwitchRequest.js":70}],83:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _SwitchRequestJs = _dereq_('../SwitchRequest.js');

var _SwitchRequestJs2 = _interopRequireDefault(_SwitchRequestJs);

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function LiveEdgeWithTimeSynchronizationRule(config) {

    var instance = undefined;
    var context = this.context;
    var timelineConverter = config.timelineConverter;

    // if the time has been synchronized correctly (which it must have been
    // to end up executing this rule), the last entry in the DVR window
    // should be the live edge. if that is incorrect for whatever reason,
    // playback will fail to start and some other action should be taken.
    function execute(rulesContext, callback) {
        var representationInfo = rulesContext.getTrackInfo();
        var liveEdgeInitialSearchPosition = representationInfo.DVRWindow.end;
        var p = _SwitchRequestJs2['default'].DEFAULT;

        if (representationInfo.useCalculatedLiveEdgeTime) {
            //By default an expected live edge is the end of the last segment.
            // A calculated live edge ('end' property of a range returned by TimelineConverter.calcSegmentAvailabilityRange)
            // is used as an initial point for finding the actual live edge.
            // But for SegmentTimeline mpds (w/o a negative @r) the end of the
            // last segment is the actual live edge. At the same time, calculated live edge is an expected live edge.
            // Thus, we need to switch an expected live edge and actual live edge for SegmentTimeline streams.
            var actualLiveEdge = timelineConverter.getExpectedLiveEdge();
            timelineConverter.setExpectedLiveEdge(liveEdgeInitialSearchPosition);
            callback((0, _SwitchRequestJs2['default'])(context).create(actualLiveEdge, p));
        } else {
            callback((0, _SwitchRequestJs2['default'])(context).create(liveEdgeInitialSearchPosition, p));
        }
    }

    instance = {
        execute: execute
    };

    return instance;
}

LiveEdgeWithTimeSynchronizationRule.__dashjs_factory_name = 'LiveEdgeWithTimeSynchronizationRule';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(LiveEdgeWithTimeSynchronizationRule);
module.exports = exports['default'];

},{"../../../core/FactoryMaker.js":7,"../SwitchRequest.js":70}],84:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _dashUtilsTimelineConverterJs = _dereq_('../../../dash/utils/TimelineConverter.js');

var _dashUtilsTimelineConverterJs2 = _interopRequireDefault(_dashUtilsTimelineConverterJs);

var _LiveEdgeBinarySearchRuleJs = _dereq_('./LiveEdgeBinarySearchRule.js');

var _LiveEdgeBinarySearchRuleJs2 = _interopRequireDefault(_LiveEdgeBinarySearchRuleJs);

var _LiveEdgeWithTimeSynchronizationRuleJs = _dereq_('./LiveEdgeWithTimeSynchronizationRule.js');

var _LiveEdgeWithTimeSynchronizationRuleJs2 = _interopRequireDefault(_LiveEdgeWithTimeSynchronizationRuleJs);

var _dashDashAdapterJs = _dereq_('../../../dash/DashAdapter.js');

var _dashDashAdapterJs2 = _interopRequireDefault(_dashDashAdapterJs);

var TIME_SYNCHRONIZED_RULES = 'withAccurateTimeSourceRules';
var BEST_GUESS_RULES = 'bestGuestRules';

function SynchronizationRulesCollection() {

    var context = this.context;

    var instance = undefined,
        withAccurateTimeSourceRules = undefined,
        bestGuestRules = undefined;

    function initialize() {
        withAccurateTimeSourceRules = [];
        bestGuestRules = [];

        withAccurateTimeSourceRules.push((0, _LiveEdgeWithTimeSynchronizationRuleJs2['default'])(context).create({
            timelineConverter: (0, _dashUtilsTimelineConverterJs2['default'])(context).getInstance()
        }));

        bestGuestRules.push((0, _LiveEdgeBinarySearchRuleJs2['default'])(context).create({
            timelineConverter: (0, _dashUtilsTimelineConverterJs2['default'])(context).getInstance(),
            adapter: (0, _dashDashAdapterJs2['default'])(context).getInstance()
        }));
    }

    function getRules(type) {
        switch (type) {
            case TIME_SYNCHRONIZED_RULES:
                return withAccurateTimeSourceRules;
            case BEST_GUESS_RULES:
                return bestGuestRules;
            default:
                return null;
        }
    }

    instance = {
        initialize: initialize,
        getRules: getRules
    };

    return instance;
}

SynchronizationRulesCollection.__dashjs_factory_name = 'SynchronizationRulesCollection';
var factory = _coreFactoryMakerJs2['default'].getSingletonFactory(SynchronizationRulesCollection);
factory.TIME_SYNCHRONIZED_RULES = TIME_SYNCHRONIZED_RULES;
factory.BEST_GUESS_RULES = BEST_GUESS_RULES;
exports['default'] = factory;
module.exports = exports['default'];

},{"../../../core/FactoryMaker.js":7,"../../../dash/DashAdapter.js":11,"../../../dash/utils/TimelineConverter.js":23,"./LiveEdgeBinarySearchRule.js":82,"./LiveEdgeWithTimeSynchronizationRule.js":83}],85:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _dashModelsDashManifestModelJs = _dereq_('../../dash/models/DashManifestModel.js');

var _dashModelsDashManifestModelJs2 = _interopRequireDefault(_dashModelsDashManifestModelJs);

var _controllersBlacklistControllerJs = _dereq_('../controllers/BlacklistController.js');

var _controllersBlacklistControllerJs2 = _interopRequireDefault(_controllersBlacklistControllerJs);

var _rulesBaseUrlResolutionDVBSelectorJs = _dereq_('../rules/baseUrlResolution/DVBSelector.js');

var _rulesBaseUrlResolutionDVBSelectorJs2 = _interopRequireDefault(_rulesBaseUrlResolutionDVBSelectorJs);

var _rulesBaseUrlResolutionBasicSelectorJs = _dereq_('../rules/baseUrlResolution/BasicSelector.js');

var _rulesBaseUrlResolutionBasicSelectorJs2 = _interopRequireDefault(_rulesBaseUrlResolutionBasicSelectorJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var URL_RESOLUTION_FAILED_GENERIC_ERROR_CODE = 1;
var URL_RESOLUTION_FAILED_GENERIC_ERROR_MESSAGE = 'Failed to resolve a valid URL';

function BaseURLSelector() {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();
    var dashManifestModel = (0, _dashModelsDashManifestModelJs2['default'])(context).getInstance();

    var instance = undefined,
        serviceLocationBlacklistController = undefined,
        basicSelector = undefined,
        dvbSelector = undefined,
        selector = undefined;

    function setup() {
        serviceLocationBlacklistController = (0, _controllersBlacklistControllerJs2['default'])(context).create({
            updateEventName: _coreEventsEventsJs2['default'].SERVICE_LOCATION_BLACKLIST_CHANGED,
            loadFailedEventName: _coreEventsEventsJs2['default'].FRAGMENT_LOADING_COMPLETED
        });

        basicSelector = (0, _rulesBaseUrlResolutionBasicSelectorJs2['default'])(context).create({
            blacklistController: serviceLocationBlacklistController
        });

        dvbSelector = (0, _rulesBaseUrlResolutionDVBSelectorJs2['default'])(context).create({
            blacklistController: serviceLocationBlacklistController
        });

        selector = basicSelector;
    }

    function chooseSelectorFromManifest(manifest) {
        if (dashManifestModel.getIsDVB(manifest)) {
            selector = dvbSelector;
        } else {
            selector = basicSelector;
        }
    }

    function select(data) {
        var baseUrls = data.baseUrls;
        var selectedIdx = data.selectedIdx;

        // Once a random selection has been carried out amongst a group of BaseURLs with the same
        // @priority attribute value, then that choice should be re-used if the selection needs to be made again
        // unless the blacklist has been modified or the available BaseURLs have changed.
        if (!isNaN(selectedIdx)) {
            return baseUrls[selectedIdx];
        }

        var selectedBaseUrl = selector.select(baseUrls);

        if (!selectedBaseUrl) {
            eventBus.trigger(_coreEventsEventsJs2['default'].URL_RESOLUTION_FAILED, {
                error: new Error(URL_RESOLUTION_FAILED_GENERIC_ERROR_CODE, URL_RESOLUTION_FAILED_GENERIC_ERROR_MESSAGE)
            });

            return;
        }

        data.selectedIdx = baseUrls.indexOf(selectedBaseUrl);

        return selectedBaseUrl;
    }

    function reset() {
        serviceLocationBlacklistController.reset();
    }

    instance = {
        chooseSelectorFromManifest: chooseSelectorFromManifest,
        select: select,
        reset: reset
    };

    setup();

    return instance;
}

BaseURLSelector.__dashjs_factory_name = 'BaseURLSelector';
var factory = _coreFactoryMakerJs2['default'].getClassFactory(BaseURLSelector);
factory.URL_RESOLUTION_FAILED_GENERIC_ERROR_CODE = URL_RESOLUTION_FAILED_GENERIC_ERROR_CODE;
factory.URL_RESOLUTION_FAILED_GENERIC_ERROR_MESSAGE = URL_RESOLUTION_FAILED_GENERIC_ERROR_MESSAGE;
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../../dash/models/DashManifestModel.js":17,"../controllers/BlacklistController.js":48,"../rules/baseUrlResolution/BasicSelector.js":78,"../rules/baseUrlResolution/DVBSelector.js":79}],86:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _IsoFileJs = _dereq_('./IsoFile.js');

var _IsoFileJs2 = _interopRequireDefault(_IsoFileJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _codemIsoboxer = _dereq_('codem-isoboxer');

var _codemIsoboxer2 = _interopRequireDefault(_codemIsoboxer);

var SAMPLE_IS_NON_SYNC = 0x10000;

function BoxParser() /*config*/{

    var instance = undefined;
    var context = this.context;

    /**
     * @param {ArrayBuffer} data
     * @returns {@link IsoFile}
     * @memberof BoxParser#
     */
    function parse(data) {
        if (!data) return null;

        if (data.fileStart === undefined) {
            data.fileStart = 0;
        }

        var parsedFile = _codemIsoboxer2['default'].parseBuffer(data);
        var dashIsoFile = (0, _IsoFileJs2['default'])(context).create();

        dashIsoFile.setData(parsedFile);

        return dashIsoFile;
    }
    function avccExtraData(data) {
        if (!data) return null;

        var isoFile = parse(data);
        var stsdBox = isoFile.getBox('stsd');
        if (stsdBox && stsdBox.entry_count) {
            for (var i = 0; i < stsdBox.entry_count; i++) {
                if (stsdBox.entries[i].type == 'avc1') {
                    return stsdBox.entries[i].config;
                }
            }
        }
    }

    function isIDR(data, nalLenSize) {
        if (!data) return;

        var pos = 0;
        var len = data.byteLength;
        while (len - pos >= nalLenSize) {
            var nalLen;
            switch (nalLenSize) {
                case 1:
                    nalLen = data.getUint8(pos);break;
                case 2:
                    nalLen = data.getUint16(pos);break;
                case 3:
                    nalLen = data.getUint24(pos);break;
                case 4:
                    nalLen = data.getUint32(pos);break;
            }
            pos += nalLenSize;
            if (!nalLen) {
                continue;
            }
            var t = data.getUint8(pos);
            pos += nalLen;
            if ((t & 0x1f) == 5) {
                // IDR NAL.
                return true;
            }
        }
    }

    function getSyncSamples(extraData, data) {
        if (!data || !extraData) return;

        var isoFile = parse(data);
        var mdatBox = isoFile.getBox('mdat');
        var trunBox = isoFile.getBox('trun');
        if (!trunBox || !trunBox.samples) {
            return;
        }
        var samples = trunBox.samples;
        var pos = mdatBox.offset + 8;
        var nalLenSize = (extraData.getUint8(8 + 4) & 3) + 1;
        var res = [];
        for (var i = 0, len = samples.length; i < len; i++) {
            if (!(samples[i].sample_flags & SAMPLE_IS_NON_SYNC)) {
                var s = { index: i };
                if (isIDR(new DataView(data, pos, samples[i].sample_size), nalLenSize)) s.isIDR = true;
                res.push(s);
            }
            pos += samples[i].sample_size;
        }
        return res;
    }

    instance = {
        parse: parse,
        avccExtraData: avccExtraData,
        getSyncSamples: getSyncSamples
    };

    return instance;
}
BoxParser.__dashjs_factory_name = 'BoxParser';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(BoxParser);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7,"./IsoFile.js":91,"codem-isoboxer":4}],87:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function Capabilities() {

    var instance = undefined,
        encryptedMediaSupported = undefined;

    function setup() {
        encryptedMediaSupported = false;
    }

    function supportsMediaSource() {
        var hasWebKit = ('WebKitMediaSource' in window);
        var hasMediaSource = ('MediaSource' in window);

        return hasWebKit || hasMediaSource;
    }

    /**
     * Returns whether Encrypted Media Extensions are supported on this
     * user agent
     *
     * @return {boolean} true if EME is supported, false otherwise
     */
    function supportsEncryptedMedia() {
        return encryptedMediaSupported;
    }

    function setEncryptedMediaSupported(value) {
        encryptedMediaSupported = value;
    }

    function supportsCodec(element, codec) {

        if (!(element instanceof HTMLMediaElement)) {
            throw 'element must be of type HTMLMediaElement.';
        }

        var canPlay = element.canPlayType(codec);
        return canPlay === 'probably' || canPlay === 'maybe';
    }

    instance = {
        supportsMediaSource: supportsMediaSource,
        supportsEncryptedMedia: supportsEncryptedMedia,
        supportsCodec: supportsCodec,
        setEncryptedMediaSupported: setEncryptedMediaSupported
    };

    setup();

    return instance;
}
Capabilities.__dashjs_factory_name = 'Capabilities';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(Capabilities);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7}],88:[function(_dereq_,module,exports){
/**
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and contributor
* rights, including patent rights, and no such rights are granted under this license.
*
* Copyright (c) 2013, Dash Industry Forum.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*  * Redistributions of source code must retain the above copyright notice, this
*  list of conditions and the following disclaimer.
*  * Redistributions in binary form must reproduce the above copyright notice,
*  this list of conditions and the following disclaimer in the documentation and/or
*  other materials provided with the distribution.
*  * Neither the name of Dash Industry Forum nor the names of its
*  contributors may be used to endorse or promote products derived from this software
*  without specific prior written permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
*  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
*  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
*  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
*  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
*  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
*  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
*  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
*  POSSIBILITY OF SUCH DAMAGE.
*/
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function CustomTimeRanges() /*config*/{
    var customTimeRangeArray = [];
    var length = 0;

    function add(start, end) {
        var i = 0;

        for (i = 0; i < this.customTimeRangeArray.length && start > this.customTimeRangeArray[i].start; i++);

        this.customTimeRangeArray.splice(i, 0, { start: start, end: end });

        for (i = 0; i < this.customTimeRangeArray.length - 1; i++) {
            if (this.mergeRanges(i, i + 1)) {
                i--;
            }
        }
        this.length = this.customTimeRangeArray.length;
    }

    function clear() {
        this.customTimeRangeArray = [];
        this.length = 0;
    }

    function remove(start, end) {
        for (var i = 0; i < this.customTimeRangeArray.length; i++) {
            if (start <= this.customTimeRangeArray[i].start && end >= this.customTimeRangeArray[i].end) {
                //      |--------------Range i-------|
                //|---------------Range to remove ---------------|
                //    or
                //|--------------Range i-------|
                //|--------------Range to remove ---------------|
                //    or
                //                 |--------------Range i-------|
                //|--------------Range to remove ---------------|
                this.customTimeRangeArray.splice(i, 1);
                i--;
            } else if (start > this.customTimeRangeArray[i].start && end < this.customTimeRangeArray[i].end) {
                //|-----------------Range i----------------|
                //        |-------Range to remove -----|
                this.customTimeRangeArray.splice(i + 1, 0, { start: end, end: this.customTimeRangeArray[i].end });
                this.customTimeRangeArray[i].end = start;
                break;
            } else if (start > this.customTimeRangeArray[i].start && start < this.customTimeRangeArray[i].end) {
                //|-----------Range i----------|
                //                    |---------Range to remove --------|
                //    or
                //|-----------------Range i----------------|
                //            |-------Range to remove -----|
                this.customTimeRangeArray[i].end = start;
            } else if (end > this.customTimeRangeArray[i].start && end < this.customTimeRangeArray[i].end) {
                //                     |-----------Range i----------|
                //|---------Range to remove --------|
                //            or
                //|-----------------Range i----------------|
                //|-------Range to remove -----|
                this.customTimeRangeArray[i].start = end;
            }
        }

        this.length = this.customTimeRangeArray.length;
    }

    function mergeRanges(rangeIndex1, rangeIndex2) {
        var range1 = this.customTimeRangeArray[rangeIndex1];
        var range2 = this.customTimeRangeArray[rangeIndex2];

        if (range1.start <= range2.start && range2.start <= range1.end && range1.end <= range2.end) {
            //|-----------Range1----------|
            //                    |-----------Range2----------|
            range1.end = range2.end;
            this.customTimeRangeArray.splice(rangeIndex2, 1);
            return true;
        } else if (range2.start <= range1.start && range1.start <= range2.end && range2.end <= range1.end) {
            //                |-----------Range1----------|
            //|-----------Range2----------|
            range1.start = range2.start;
            this.customTimeRangeArray.splice(rangeIndex2, 1);
            return true;
        } else if (range2.start <= range1.start && range1.start <= range2.end && range1.end <= range2.end) {
            //      |--------Range1-------|
            //|---------------Range2--------------|
            this.customTimeRangeArray.splice(rangeIndex1, 1);
            return true;
        } else if (range1.start <= range2.start && range2.start <= range1.end && range2.end <= range1.end) {
            //|-----------------Range1--------------|
            //        |-----------Range2----------|
            this.customTimeRangeArray.splice(rangeIndex2, 1);
            return true;
        }
        return false;
    }

    function start(index) {
        return this.customTimeRangeArray[index].start;
    }

    function end(index) {
        return this.customTimeRangeArray[index].end;
    }

    return {
        customTimeRangeArray: customTimeRangeArray,
        length: length,
        add: add,
        clear: clear,
        remove: remove,
        mergeRanges: mergeRanges,
        start: start,
        end: end
    };
}
CustomTimeRanges.__dashjs_factory_name = 'CustomTimeRanges';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(CustomTimeRanges);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7}],89:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _modelsMediaPlayerModelJs = _dereq_('../models/MediaPlayerModel.js');

var _modelsMediaPlayerModelJs2 = _interopRequireDefault(_modelsMediaPlayerModelJs);

var _coreDebugJs = _dereq_('../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var legacyKeysAndReplacements = [{ oldKey: 'dashjs_vbitrate', newKey: 'dashjs_video_bitrate' }, { oldKey: 'dashjs_abitrate', newKey: 'dashjs_audio_bitrate' }, { oldKey: 'dashjs_vsettings', newKey: 'dashjs_video_settings' }, { oldKey: 'dashjs_asettings', newKey: 'dashjs_audio_settings' }];

var LOCAL_STORAGE_BITRATE_KEY_TEMPLATE = 'dashjs_?_bitrate';
var LOCAL_STORAGE_SETTINGS_KEY_TEMPLATE = 'dashjs_?_settings';

var STORAGE_TYPE_LOCAL = 'localStorage';
var STORAGE_TYPE_SESSION = 'sessionStorage';

function DOMStorage() {

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;

    var instance = undefined,
        supported = undefined,
        mediaPlayerModel = undefined;

    //type can be local, session
    function isSupported(type) {
        if (supported !== undefined) return supported;

        supported = false;

        var testKey = '1';
        var testValue = '1';
        var storage;

        try {
            if (typeof window !== 'undefined') {
                storage = window[type];
            }
        } catch (error) {
            log('Warning: DOMStorage access denied: ' + error.message);
            return supported;
        }

        if (!storage || type !== STORAGE_TYPE_LOCAL && type !== STORAGE_TYPE_SESSION) {
            return supported;
        }

        /* When Safari (OS X or iOS) is in private browsing mode, it appears as though localStorage is available, but trying to call setItem throws an exception.
         http://stackoverflow.com/questions/14555347/html5-localstorage-error-with-safari-quota-exceeded-err-dom-exception-22-an
          Check if the storage can be used
         */
        try {
            storage.setItem(testKey, testValue);
            storage.removeItem(testKey);
            supported = true;
        } catch (error) {
            log('Warning: DOMStorage is supported, but cannot be used: ' + error.message);
        }

        return supported;
    }

    function translateLegacyKeys() {
        if (isSupported(STORAGE_TYPE_LOCAL)) {
            legacyKeysAndReplacements.forEach(function (entry) {
                var value = localStorage.getItem(entry.oldKey);

                if (value) {
                    localStorage.removeItem(entry.oldKey);

                    try {
                        localStorage.setItem(entry.newKey, value);
                    } catch (e) {
                        log(e.message);
                    }
                }
            });
        }
    }

    function setup() {
        mediaPlayerModel = (0, _modelsMediaPlayerModelJs2['default'])(context).getInstance();

        translateLegacyKeys();
    }

    // Return current epoch time, ms, rounded to the nearest 10m to avoid fingerprinting user
    function getTimestamp() {
        var ten_minutes_ms = 60 * 1000 * 10;
        return Math.round(new Date().getTime() / ten_minutes_ms) * ten_minutes_ms;
    }

    function canStore(storageType, key) {
        return isSupported(storageType) && mediaPlayerModel['get' + key + 'CachingInfo']().enabled;
    }

    function getSavedMediaSettings(type) {
        //Checks local storage to see if there is valid, non-expired media settings
        if (!canStore(STORAGE_TYPE_LOCAL, 'LastMediaSettings')) return null;

        var key = LOCAL_STORAGE_SETTINGS_KEY_TEMPLATE.replace(/\?/, type);
        var obj = JSON.parse(localStorage.getItem(key)) || {};
        var isExpired = new Date().getTime() - parseInt(obj.timestamp, 10) >= mediaPlayerModel.getLastMediaSettingsCachingInfo().ttl || false;
        var settings = obj.settings;

        if (isExpired) {
            localStorage.removeItem(key);
            settings = null;
        }

        return settings;
    }

    function getSavedBitrateSettings(type) {
        var savedBitrate = NaN;
        //Checks local storage to see if there is valid, non-expired bit rate
        //hinting from the last play session to use as a starting bit rate.
        if (canStore(STORAGE_TYPE_LOCAL, 'LastBitrate')) {
            var key = LOCAL_STORAGE_BITRATE_KEY_TEMPLATE.replace(/\?/, type);
            var obj = JSON.parse(localStorage.getItem(key)) || {};
            var isExpired = new Date().getTime() - parseInt(obj.timestamp, 10) >= mediaPlayerModel.getLastBitrateCachingInfo().ttl || false;
            var bitrate = parseInt(obj.bitrate, 10);

            if (!isNaN(bitrate) && !isExpired) {
                savedBitrate = bitrate;
                log('Last saved bitrate for ' + type + ' was ' + bitrate);
            } else if (isExpired) {
                localStorage.removeItem(key);
            }
        }
        return savedBitrate;
    }

    function setSavedMediaSettings(type, value) {
        if (canStore(STORAGE_TYPE_LOCAL, 'LastMediaSettings')) {
            var key = LOCAL_STORAGE_SETTINGS_KEY_TEMPLATE.replace(/\?/, type);
            try {
                localStorage.setItem(key, JSON.stringify({ settings: value, timestamp: getTimestamp() }));
            } catch (e) {
                log(e.message);
            }
        }
    }

    function setSavedBitrateSettings(type, bitrate) {
        if (canStore(STORAGE_TYPE_LOCAL, 'LastBitrate') && bitrate) {
            var key = LOCAL_STORAGE_BITRATE_KEY_TEMPLATE.replace(/\?/, type);
            try {
                localStorage.setItem(key, JSON.stringify({ bitrate: bitrate / 1000, timestamp: getTimestamp() }));
            } catch (e) {
                log(e.message);
            }
        }
    }

    instance = {
        getSavedBitrateSettings: getSavedBitrateSettings,
        setSavedBitrateSettings: setSavedBitrateSettings,
        getSavedMediaSettings: getSavedMediaSettings,
        setSavedMediaSettings: setSavedMediaSettings,
        isSupported: isSupported
    };

    setup();
    return instance;
}

DOMStorage.__dashjs_factory_name = 'DOMStorage';
var factory = _coreFactoryMakerJs2['default'].getSingletonFactory(DOMStorage);
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/Debug.js":5,"../../core/FactoryMaker.js":7,"../models/MediaPlayerModel.js":64}],90:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var CAPABILITY_ERROR_MEDIASOURCE = 'mediasource';
var CAPABILITY_ERROR_MEDIAKEYS = 'mediakeys';

var DOWNLOAD_ERROR_ID_MANIFEST = 'manifest';
var DOWNLOAD_ERROR_ID_SIDX = 'SIDX';
var DOWNLOAD_ERROR_ID_CONTENT = 'content';
var DOWNLOAD_ERROR_ID_INITIALIZATION = 'initialization';
var DOWNLOAD_ERROR_ID_XLINK = 'xlink';

var MANIFEST_ERROR_ID_CODEC = 'codec';
var MANIFEST_ERROR_ID_PARSE = 'parse';
var MANIFEST_ERROR_ID_NOSTREAMS = 'nostreams';

var TIMED_TEXT_ERROR_ID_PARSE = 'parse';

function ErrorHandler() {

    var instance = undefined;
    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    // "mediasource"|"mediakeys"
    function capabilityError(err) {
        eventBus.trigger(_coreEventsEventsJs2['default'].ERROR, { error: 'capability', event: err });
    }

    // {id: "manifest"|"SIDX"|"content"|"initialization"|"xlink", url: "", request: {XMLHttpRequest instance}}
    function downloadError(id, url, request) {
        eventBus.trigger(_coreEventsEventsJs2['default'].ERROR, { error: 'download', event: { id: id, url: url, request: request } });
    }

    // {message: "", id: "codec"|"parse"|"nostreams", manifest: {parsed manifest}}
    function manifestError(message, id, manifest) {
        eventBus.trigger(_coreEventsEventsJs2['default'].ERROR, { error: 'manifestError', event: { message: message, id: id, manifest: manifest } });
    }

    // {message: '', id: 'parse', cc: ''}
    function timedTextError(message, id, ccContent) {
        eventBus.trigger(_coreEventsEventsJs2['default'].ERROR, { error: 'cc', event: { message: message, id: id, cc: ccContent } });
    }

    function mediaSourceError(err) {
        eventBus.trigger(_coreEventsEventsJs2['default'].ERROR, { error: 'mediasource', event: err });
    }

    function mediaKeySessionError(err) {
        eventBus.trigger(_coreEventsEventsJs2['default'].ERROR, { error: 'key_session', event: err });
    }

    function mediaKeyMessageError(err) {
        eventBus.trigger(_coreEventsEventsJs2['default'].ERROR, { error: 'key_message', event: err });
    }

    instance = {
        capabilityError: capabilityError,
        downloadError: downloadError,
        manifestError: manifestError,
        timedTextError: timedTextError,
        mediaSourceError: mediaSourceError,
        mediaKeySessionError: mediaKeySessionError,
        mediaKeyMessageError: mediaKeyMessageError
    };

    return instance;
}

ErrorHandler.__dashjs_factory_name = 'ErrorHandler';

var factory = _coreFactoryMakerJs2['default'].getSingletonFactory(ErrorHandler);

factory.CAPABILITY_ERROR_MEDIASOURCE = CAPABILITY_ERROR_MEDIASOURCE;
factory.CAPABILITY_ERROR_MEDIAKEYS = CAPABILITY_ERROR_MEDIAKEYS;
factory.DOWNLOAD_ERROR_ID_MANIFEST = DOWNLOAD_ERROR_ID_MANIFEST;
factory.DOWNLOAD_ERROR_ID_SIDX = DOWNLOAD_ERROR_ID_SIDX;
factory.DOWNLOAD_ERROR_ID_CONTENT = DOWNLOAD_ERROR_ID_CONTENT;
factory.DOWNLOAD_ERROR_ID_INITIALIZATION = DOWNLOAD_ERROR_ID_INITIALIZATION;
factory.DOWNLOAD_ERROR_ID_XLINK = DOWNLOAD_ERROR_ID_XLINK;
factory.MANIFEST_ERROR_ID_CODEC = MANIFEST_ERROR_ID_CODEC;
factory.MANIFEST_ERROR_ID_PARSE = MANIFEST_ERROR_ID_PARSE;
factory.MANIFEST_ERROR_ID_NOSTREAMS = MANIFEST_ERROR_ID_NOSTREAMS;
factory.TIMED_TEXT_ERROR_ID_PARSE = TIMED_TEXT_ERROR_ID_PARSE;

exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9}],91:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _voIsoBoxJs = _dereq_('../vo/IsoBox.js');

var _voIsoBoxJs2 = _interopRequireDefault(_voIsoBoxJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function IsoFile() {

    var instance = undefined,
        parsedIsoFile = undefined,
        commonProps = undefined,
        sidxProps = undefined,
        sidxRefProps = undefined,
        stsdProps = undefined,
        emsgProps = undefined,
        mdhdProps = undefined,
        mfhdProps = undefined,
        tfhdProps = undefined,
        tfdtProps = undefined,
        trunProps = undefined,
        trunSampleProps = undefined;

    /**
    * @param {string} type
    * @returns {@link IsoBox}
    * @memberof IsoFile#
    */
    function getBox(type) {
        if (!type || !parsedIsoFile || !parsedIsoFile.boxes || parsedIsoFile.boxes.length === 0) return null;

        return convertToDashIsoBox(parsedIsoFile.fetch(type));
    }

    /**
    * @param {string} type
    * @returns {Array} array of {@link IsoBox}
    * @memberof IsoFile#
    */
    function getBoxes(type) {
        var boxData = parsedIsoFile.fetchAll(type);
        var boxes = [];
        var box;

        for (var i = 0, ln = boxData.length; i < ln; i++) {
            box = convertToDashIsoBox(boxData[i]);

            if (box) {
                boxes.push(box);
            }
        }

        return boxes;
    }

    /**
    * @param {string} value
    * @memberof IsoFile#
    */
    function setData(value) {
        parsedIsoFile = value;
    }

    /**
    * @returns {@link IsoBox}
    * @memberof IsoFile#
    */
    function getLastBox() {
        if (!parsedIsoFile || !parsedIsoFile.boxes || !parsedIsoFile.boxes.length) return null;

        var type = parsedIsoFile.boxes[parsedIsoFile.boxes.length - 1].type;
        var boxes = getBoxes(type);

        return boxes[boxes.length - 1];
    }

    /**
    * @returns {Number}
    * @memberof IsoFile#
    */
    function getOffset() {
        return parsedIsoFile._cursor.offset;
    }

    function setup() {
        commonProps = {
            offset: '_offset',
            size: 'size',
            type: 'type'
        };

        sidxProps = {
            references: 'references',
            timescale: 'timescale',
            earliest_presentation_time: 'earliest_presentation_time',
            first_offset: 'first_offset'
        };

        sidxRefProps = {
            reference_type: 'reference_type',
            referenced_size: 'referenced_size',
            subsegment_duration: 'subsegment_duration'
        };

        stsdProps = {
            entries: 'entries',
            entry_count: 'entry_count'
        };

        emsgProps = {
            id: 'id',
            value: 'value',
            timescale: 'timescale',
            scheme_id_uri: 'scheme_id_uri',
            presentation_time_delta: 'presentation_time_delta',
            event_duration: 'event_duration',
            message_data: 'message_data'
        };

        mdhdProps = {
            timescale: 'timescale'
        };

        mfhdProps = {
            sequence_number: 'sequence_number'
        };

        tfhdProps = {
            base_data_offset: 'base_data_offset',
            sample_description_index: 'sample_description_index',
            default_sample_duration: 'default_sample_duration',
            default_sample_size: 'default_sample_size',
            default_sample_flags: 'default_sample_flags',
            flags: 'flags'
        };

        tfdtProps = {
            version: 'version',
            baseMediaDecodeTime: 'baseMediaDecodeTime',
            flags: 'flags'
        };

        trunProps = {
            sample_count: 'sample_count',
            first_sample_flags: 'first_sample_flags',
            data_offset: 'data_offset',
            flags: 'flags',
            samples: 'samples'
        };

        trunSampleProps = {
            sample_size: 'sample_size',
            sample_duration: 'sample_duration',
            sample_composition_time_offset: 'sample_composition_time_offset'
        };
    }

    function copyProps(from, to, props) {
        for (var prop in props) {
            to[prop] = from[props[prop]];
        }
    }

    function convertToDashIsoBox(boxData) {
        if (!boxData) return null;

        var box = new _voIsoBoxJs2['default']();
        var i, ln;

        copyProps(boxData, box, commonProps);

        if (boxData.hasOwnProperty('_incomplete')) {
            box.isComplete = !boxData._incomplete;
        }

        switch (box.type) {
            case 'sidx':
                copyProps(boxData, box, sidxProps);
                if (box.references) {
                    for (i = 0, ln = box.references.length; i < ln; i++) {
                        copyProps(boxData.references[i], box.references[i], sidxRefProps);
                    }
                }
                break;
            case 'stsd':
                copyProps(boxData, box, stsdProps);
                break;
            case 'emsg':
                copyProps(boxData, box, emsgProps);
                break;
            case 'mdhd':
                copyProps(boxData, box, mdhdProps);
                break;
            case 'mfhd':
                copyProps(boxData, box, mfhdProps);
                break;
            case 'tfhd':
                copyProps(boxData, box, tfhdProps);
                break;
            case 'tfdt':
                copyProps(boxData, box, tfdtProps);
                break;
            case 'trun':
                copyProps(boxData, box, trunProps);
                if (box.samples) {
                    for (i = 0, ln = box.samples.length; i < ln; i++) {
                        copyProps(boxData.samples[i], box.samples[i], trunSampleProps);
                    }
                }
                break;
        }

        return box;
    }

    instance = {
        getBox: getBox,
        getBoxes: getBoxes,
        setData: setData,
        getLastBox: getLastBox,
        getOffset: getOffset
    };

    setup();

    return instance;
}
IsoFile.__dashjs_factory_name = 'IsoFile';
exports['default'] = _coreFactoryMakerJs2['default'].getClassFactory(IsoFile);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7,"../vo/IsoBox.js":103}],92:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _rulesSynchronizationSynchronizationRulesCollectionJs = _dereq_('../rules/synchronization/SynchronizationRulesCollection.js');

var _rulesSynchronizationSynchronizationRulesCollectionJs2 = _interopRequireDefault(_rulesSynchronizationSynchronizationRulesCollectionJs);

var _voErrorJs = _dereq_('../vo/Error.js');

var _voErrorJs2 = _interopRequireDefault(_voErrorJs);

var _coreEventBusJs = _dereq_('../../core/EventBus.js');

var _coreEventBusJs2 = _interopRequireDefault(_coreEventBusJs);

var _coreEventsEventsJs = _dereq_('../../core/events/Events.js');

var _coreEventsEventsJs2 = _interopRequireDefault(_coreEventsEventsJs);

var _rulesRulesControllerJs = _dereq_('../rules/RulesController.js');

var _rulesRulesControllerJs2 = _interopRequireDefault(_rulesRulesControllerJs);

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var LIVE_EDGE_NOT_FOUND_ERROR_CODE = 1;

function LiveEdgeFinder() {

    var context = this.context;
    var eventBus = (0, _coreEventBusJs2['default'])(context).getInstance();

    var instance = undefined,
        timelineConverter = undefined,
        streamProcessor = undefined,
        rulesController = undefined,
        isSearchStarted = undefined,
        searchStartTime = undefined,
        rules = undefined,
        liveEdge = undefined,
        ruleSet = undefined;

    function initialize(TimelineConverter, StreamProcessor) {
        timelineConverter = TimelineConverter;
        streamProcessor = StreamProcessor;
        isSearchStarted = false;
        searchStartTime = NaN;
        liveEdge = null;
        rulesController = (0, _rulesRulesControllerJs2['default'])(context).getInstance();
        ruleSet = _rulesSynchronizationSynchronizationRulesCollectionJs2['default'].BEST_GUESS_RULES;
        eventBus.on(_coreEventsEventsJs2['default'].STREAM_INITIALIZED, onStreamInitialized, this);
    }

    function abortSearch() {
        isSearchStarted = false;
        searchStartTime = NaN;
    }

    function getLiveEdge() {
        return liveEdge;
    }

    function reset() {
        eventBus.off(_coreEventsEventsJs2['default'].STREAM_INITIALIZED, onStreamInitialized, this);
        abortSearch();
        liveEdge = null;
        timelineConverter = null;
        streamProcessor = null;
        isSearchStarted = false;
        searchStartTime = NaN;
        ruleSet = null;
        rulesController = null;
    }

    function onSearchCompleted(req) {
        var searchTime = (new Date().getTime() - searchStartTime) / 1000;
        liveEdge = req.value;
        eventBus.trigger(_coreEventsEventsJs2['default'].LIVE_EDGE_SEARCH_COMPLETED, { liveEdge: liveEdge, searchTime: searchTime, error: liveEdge === null ? new _voErrorJs2['default'](LIVE_EDGE_NOT_FOUND_ERROR_CODE, 'live edge has not been found', null) : null });
    }

    function onStreamInitialized(e) {

        if (!streamProcessor.isDynamic() || isSearchStarted || e.error) {
            return;
        }

        ruleSet = timelineConverter.isTimeSyncCompleted() ? _rulesSynchronizationSynchronizationRulesCollectionJs2['default'].TIME_SYNCHRONIZED_RULES : _rulesSynchronizationSynchronizationRulesCollectionJs2['default'].BEST_GUESS_RULES;

        rules = (0, _rulesSynchronizationSynchronizationRulesCollectionJs2['default'])(context).getInstance().getRules(ruleSet);
        isSearchStarted = true;
        searchStartTime = new Date().getTime();

        rulesController.applyRules(rules, streamProcessor, onSearchCompleted, null, function (currentValue, newValue) {
            return newValue;
        });
    }

    instance = {
        initialize: initialize,
        abortSearch: abortSearch,
        getLiveEdge: getLiveEdge,
        reset: reset
    };

    return instance;
}
LiveEdgeFinder.__dashjs_factory_name = 'LiveEdgeFinder';
var factory = _coreFactoryMakerJs2['default'].getSingletonFactory(LiveEdgeFinder);
factory.LIVE_EDGE_NOT_FOUND_ERROR_CODE = LIVE_EDGE_NOT_FOUND_ERROR_CODE;
exports['default'] = factory;
module.exports = exports['default'];

},{"../../core/EventBus.js":6,"../../core/FactoryMaker.js":7,"../../core/events/Events.js":9,"../rules/RulesController.js":69,"../rules/synchronization/SynchronizationRulesCollection.js":84,"../vo/Error.js":100}],93:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

/**
 * @Module ObjectUtils
 * @description Provides utility functions for objects
 */
function ObjectUtils() {

  var instance = undefined;

  /**
   * Returns true if objects resolve to the same string. Only really useful
   * when the user controls the object generation
   * @return {boolean}
   * @memberof module:ObjectUtils
   * @instance
   */
  function areSimpleEquivalent(obj1, obj2) {
    return JSON.stringify(obj1) === JSON.stringify(obj2);
  }

  instance = {
    areSimpleEquivalent: areSimpleEquivalent
  };

  return instance;
}

ObjectUtils.__dashjs_factory_name = 'ObjectUtils';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(ObjectUtils);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7}],94:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

function RequestModifier() {

    var instance = undefined;

    function modifyRequestURL(url) {
        return url;
    }

    function modifyRequestHeader(request) {
        return request;
    }

    instance = {
        modifyRequestURL: modifyRequestURL,
        modifyRequestHeader: modifyRequestHeader
    };

    return instance;
}

RequestModifier.__dashjs_factory_name = 'RequestModifier';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(RequestModifier);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7}],95:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _externalsXml2jsonJs = _dereq_('../../../externals/xml2json.js');

var _externalsXml2jsonJs2 = _interopRequireDefault(_externalsXml2jsonJs);

var _coreDebugJs = _dereq_('../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

var SECONDS_IN_HOUR = 60 * 60; // Expression of an hour in seconds
var SECONDS_IN_MIN = 60; // Expression of a minute in seconds

function TTMLParser() {

    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;

    /*
     * This TTML parser follows "EBU-TT-D SUBTITLING DISTRIBUTION FORMAT - tech3380" spec - https://tech.ebu.ch/docs/tech/tech3380.pdf.
     * */
    var instance = undefined,
        timingRegex = undefined,
        ttml = undefined,
        // contains the whole ttml document received
    ttmlStyling = undefined,
        // contains the styling information from the document (from head following EBU-TT-D)
    ttmlLayout = undefined,
        // contains the positioning information from the document (from head following EBU-TT-D)
    fontSize = undefined,
        lineHeight = undefined,
        linePadding = undefined,
        defaultLayoutProperties = undefined,
        defaultStyleProperties = undefined,
        fontFamilies = undefined,
        textAlign = undefined,
        multiRowAlign = undefined,
        wrapOption = undefined,
        unicodeBidi = undefined,
        displayAlign = undefined,
        writingMode = undefined,
        videoModel = undefined;

    function setConfig(config) {
        if (!config) return;

        if (config.videoModel) {
            videoModel = config.videoModel;
        }
    }

    /**
     * Parse the raw data and process it to return the HTML element representing the cue.
     * Return the region to be processed and controlled (hide/show) by the caption controller.
     * @param data: raw data received from the TextSourceBuffer
     **/

    function parse(data, intervalStart, intervalEnd) {
        var tt = undefined,
            // Top element
        head = undefined,
            // head in tt
        body = undefined,
            // body in tt
        type = undefined;

        var errorMsg = '';

        var converter = new _externalsXml2jsonJs2['default']([], '', false);

        // Parse the TTML in a JSON object.
        ttml = converter.xml_str2json(data);

        if (!ttml) {
            throw 'TTML document could not be parsed';
        }

        if (videoModel.getTTMLRenderingDiv()) {
            type = 'html';
        }

        // Get the namespace if there is one defined in the JSON object.
        var ttNS = getNamespacePrefix(ttml, 'http://www.w3.org/ns/ttml');

        // Remove the namespace before each node if it exists:
        if (ttNS) {
            removeNamespacePrefix(ttml, ttNS);
        }

        // Check the document and compare to the specification (TTML and EBU-TT-D).
        tt = ttml.tt;
        if (!tt) {
            throw 'TTML document lacks tt element';
        }
        head = tt.head;
        if (!head) {
            throw 'TTML document lacks head element';
        }
        if (head.layout) {
            ttmlLayout = head.layout.region_asArray; //Mandatory in EBU-TT-D
        }
        if (head.styling) {
            ttmlStyling = head.styling.style_asArray; // Mandatory in EBU-TT-D
        }
        body = tt.body;
        if (!body) {
            throw 'TTML document lacks body element';
        }

        // Extract the cellResolution information
        var cellResolution = getCellResolution();

        // Recover the video width and height displayed by the player.
        var videoWidth = videoModel.getElement().clientWidth;
        var videoHeight = videoModel.getElement().clientHeight;

        // Compute the CellResolution unit in order to process properties using sizing (fontSize, linePadding, etc).
        var cellUnit = [videoWidth / cellResolution[0], videoHeight / cellResolution[1]];
        defaultStyleProperties['font-size'] = cellUnit[1] + 'px;';

        var regions = [];
        if (ttmlLayout) {
            for (var i = 0; i < ttmlLayout.length; i++) {
                regions.push(processRegion(JSON.parse(JSON.stringify(ttmlLayout[i])), cellUnit));
            }
        }

        // Get the namespace prefix.
        var nsttp = getNamespacePrefix(ttml.tt, 'http://www.w3.org/ns/ttml#parameter');

        // Set the framerate.
        if (ttml.tt.hasOwnProperty(nsttp + ':frameRate')) {
            ttml.tt.frameRate = parseInt(ttml.tt[nsttp + ':frameRate'], 10);
        }
        var captionArray = [];
        // Extract the div
        var divs = ttml.tt.body_asArray[0].__children;

        divs.forEach(function (div) {
            var cues = div.div.p_asArray;

            // Check if cues is not empty or undefined.
            if (!cues || cues.length === 0) {
                errorMsg = 'TTML document does not contain any cues';
            } else {

                /*** Parsing of every cue.
                 *
                 * cues: List of the cues found in the ttml parsing.
                 *       We iterate on this list.
                 * cue: Every cue is parsed individually and creates an HTML element with its style and children.
                 *
                 * pElements: all the nodes that can be found in the paragraph.
                 *
                 * ***/

                // Caption array is the final result return containing all the cues' information.
                var pStartTime;
                var pEndTime;
                var spanStartTime;
                var spanEndTime;
                cues.forEach(function (cue) {

                    // Obtain the start and end time of the cue.
                    if (cue.hasOwnProperty('begin') && cue.hasOwnProperty('end')) {
                        pStartTime = parseTimings(cue.begin);
                        pEndTime = parseTimings(cue.end);
                    } else if (cue.span.hasOwnProperty('begin') && cue.span.hasOwnProperty('end')) {
                        spanStartTime = parseTimings(cue.span.begin);
                        spanEndTime = parseTimings(cue.span.end);
                    } else {
                        errorMsg = 'TTML document has incorrect timing value';
                        throw errorMsg;
                    }
                    var cueStartTime = spanStartTime || pStartTime;
                    var cueEndTime = spanEndTime || pEndTime;

                    if (typeof intervalStart !== 'undefined' && typeof intervalEnd !== 'undefined') {
                        if (cueEndTime < intervalStart || cueStartTime > intervalEnd) {
                            log('TTML: Cue interval ' + cueStartTime + '-' + cueEndTime + ' outside sample interval ' + intervalStart + '-' + intervalEnd + '. Dropped');
                            return;
                        } else {
                            var clipped = false;
                            var origStart = cueStartTime;
                            var origEnd = cueEndTime;
                            if (cueStartTime < intervalStart) {
                                clipped = true;
                                cueStartTime = intervalStart;
                            }
                            if (cueEndTime > intervalEnd) {
                                clipped = true;
                                cueEndTime = intervalEnd;
                            }
                            if (clipped) {
                                log('TTML: Clipped cue ' + origStart + '-' + origEnd + ' to ' + cueStartTime + '-' + cueEndTime);
                            }
                        }
                    }

                    if (cue['smpte:backgroundImage'] !== undefined) {
                        var images = ttml.tt.head.metadata.image_asArray;
                        for (var j = 0; j < images.length; j++) {
                            if ('#' + images[j]['xml:id'] == cue['smpte:backgroundImage']) {
                                captionArray.push({
                                    start: cueStartTime,
                                    end: cueEndTime,
                                    id: images[j]['xml:id'],
                                    data: 'data:image/' + images[j].imagetype.toLowerCase() + ';base64, ' + images[j].__text,
                                    type: 'image'
                                });
                            }
                        }
                    } else if (type === 'html') {
                        lineHeight = {};
                        linePadding = {};
                        fontSize = {};
                        var cueID = '';
                        if (cue.hasOwnProperty('id') || cue.hasOwnProperty('xml:id')) {
                            cueID = cue['xml:id'] || cue.id;
                        }
                        // Error if timing is not specified.
                        // TODO: check with the specification what is allowed.
                        if ((isNaN(pStartTime) || isNaN(pEndTime)) && (isNaN(spanStartTime) || isNaN(spanEndTime))) {
                            errorMsg = 'TTML document has incorrect timing value';
                            throw errorMsg;
                        }

                        /**
                         * Find the region defined for the cue.
                         */
                        // properties to be put in the "captionRegion" HTML element.
                        var cueRegionProperties = constructCueRegion(cue, div.div, cellUnit);

                        /**
                         * Find the style defined for the cue.
                         */
                        // properties to be put in the "paragraph" HTML element.
                        var cueStyleProperties = constructCueStyle(cue, cellUnit);

                        /**
                         * /!\ Create the cue HTML Element containing the whole cue.
                         */
                        var styleIDs = cueStyleProperties[1];
                        cueStyleProperties = cueStyleProperties[0];

                        // Final cue HTML element.
                        var cueParagraph = document.createElement('div');
                        cueParagraph.className = styleIDs;

                        // Stock the element in the subtitle (in p) in an array (in case there are only one value).
                        var pElements = cue.__children;

                        // Create an wrapper containing the cue information about unicodeBidi and direction
                        // as they need to be defined on at this level.
                        // We append to the wrapper the cue itself.
                        var cueDirUniWrapper = constructCue(pElements, cellUnit);
                        cueDirUniWrapper.className = 'cueDirUniWrapper';

                        // If the style defines these two properties, we place them in cueContainer
                        // and delete them from the cue style so it is not added afterwards to the final cue.
                        if (arrayContains('unicode-bidi', cueStyleProperties)) {
                            cueDirUniWrapper.style.cssText += getPropertyFromArray('unicode-bidi', cueStyleProperties);
                            deletePropertyFromArray('unicode-bidi', cueStyleProperties);
                        }
                        if (arrayContains('direction', cueStyleProperties)) {
                            cueDirUniWrapper.style.cssText += getPropertyFromArray('direction', cueStyleProperties);
                            deletePropertyFromArray('direction', cueStyleProperties);
                        }

                        // Apply the linePadding property if it is specified in the cue style.
                        if (arrayContains('padding-left', cueStyleProperties) && arrayContains('padding-right', cueStyleProperties)) {
                            cueDirUniWrapper.innerHTML = applyLinePadding(cueDirUniWrapper, cueStyleProperties);
                        }

                        /**
                         * Clean and set the style and region for the cue to be returned.
                         */

                        // Remove the line padding property from being added at the "paragraph" element level.
                        if (arrayContains('padding-left', cueStyleProperties) && arrayContains('padding-right', cueStyleProperties)) {
                            deletePropertyFromArray('padding-left', cueStyleProperties);
                            deletePropertyFromArray('padding-right', cueStyleProperties);
                        }

                        // Remove the ID of the region from being added at the "paragraph" element level.
                        var regionID = '';
                        if (arrayContains('regionID', cueRegionProperties)) {
                            var wholeRegionID = getPropertyFromArray('regionID', cueRegionProperties);
                            regionID = wholeRegionID.slice(wholeRegionID.indexOf(':') + 1, wholeRegionID.length - 1);
                        }

                        // We link the p style to the finale cueParagraph element.
                        if (cueStyleProperties) {
                            cueParagraph.style.cssText = cueStyleProperties.join(' ') + 'display:flex;';
                        }
                        // We define the CSS style for the cue region.
                        if (cueRegionProperties) {
                            cueRegionProperties = cueRegionProperties.join(' ');
                        }

                        // We then place the cue wrapper inside the paragraph element.
                        cueParagraph.appendChild(cueDirUniWrapper);

                        // Final cue.
                        var finalCue = document.createElement('div');
                        finalCue.appendChild(cueParagraph);
                        finalCue.id = 'subtitle_' + cueID;
                        finalCue.style.cssText = 'position: absolute; margin: 0; display: flex; box-sizing: border-box; pointer-events: none;' + cueRegionProperties;

                        if (Object.keys(fontSize).length === 0) {
                            fontSize.defaultFontSize = '100';
                        }

                        // We add all the cue information in captionArray.
                        captionArray.push({
                            start: cueStartTime,
                            end: cueEndTime,
                            type: 'html',
                            cueHTMLElement: finalCue,
                            regions: regions,
                            regionID: regionID,
                            cueID: cueID,
                            videoHeight: videoHeight,
                            videoWidth: videoWidth,
                            cellResolution: cellResolution,
                            fontSize: fontSize || {
                                defaultFontSize: '100'
                            },
                            lineHeight: lineHeight,
                            linePadding: linePadding
                        });
                    } else {
                        var text = '';
                        var textElements = cue.__children;
                        if (textElements.length) {
                            textElements.forEach(function (el) {
                                if (el.hasOwnProperty('span')) {
                                    var spanElements = el.span.__children;
                                    spanElements.forEach(function (spanEl) {
                                        // If metadata is present, do not process.
                                        if (spanElements.hasOwnProperty('metadata')) {
                                            return;
                                        }
                                        // If the element is a string
                                        if (spanEl.hasOwnProperty('#text')) {
                                            text += spanEl['#text'].replace(/[\r\n]+/gm, ' ').trim();
                                            // If the element is a 'br' tag
                                        } else if ('br' in spanEl) {
                                                // Create a br element.
                                                text += '\n';
                                            }
                                    });
                                } else if (el.hasOwnProperty('br')) {
                                    text += '\n';
                                } else {
                                    text += el['#text'].replace(/[\r\n]+/gm, ' ').trim();
                                }
                            });
                        }

                        captionArray.push({
                            start: cueStartTime,
                            end: cueEndTime,
                            data: text,
                            type: 'text'
                        });
                    }
                });
            }
        });

        if (errorMsg !== '') {
            log(errorMsg);
        }

        if (captionArray.length > 0) {
            return captionArray;
        } else {
            throw errorMsg;
        }
    }

    function setup() {
        /*
         * This TTML parser follows "EBU-TT-D SUBTITLING DISTRIBUTION FORMAT - tech3380" spec - https://tech.ebu.ch/docs/tech/tech3380.pdf.
         * */
        timingRegex = /^([0-9][0-9]+):([0-5][0-9]):([0-5][0-9])|(60)(\.([0-9])+)?$/; // Regex defining the time
        fontSize = {};
        lineHeight = {};
        linePadding = {};
        defaultLayoutProperties = {
            'top': '85%;',
            'left': '5%;',
            'width': '90%;',
            'height': '10%;',
            'align-items': 'flex-start;',
            'overflow': 'visible;',
            '-ms-writing-mode': 'lr-tb, horizontal-tb;',
            '-webkit-writing-mode': 'horizontal-tb;',
            '-moz-writing-mode': 'horizontal-tb;',
            'writing-mode': 'horizontal-tb;'
        };
        defaultStyleProperties = {
            'color': 'rgb(255,255,255);',
            'direction': 'ltr;',
            'font-family': 'monospace, sans-serif;',
            'font-style': 'normal;',
            'line-height': 'normal;',
            'font-weight': 'normal;',
            'text-align': 'start;',
            'justify-content': 'flex-start;',
            'text-decoration': 'none;',
            'unicode-bidi': 'normal;',
            'white-space': 'normal;',
            'width': '100%;'
        };
        fontFamilies = {
            monospace: 'font-family: monospace;',
            sansSerif: 'font-family: sans-serif;',
            serif: 'font-family: serif;',
            monospaceSansSerif: 'font-family: monospace, sans-serif;',
            monospaceSerif: 'font-family: monospace, serif;',
            proportionalSansSerif: 'font-family: Arial;',
            proportionalSerif: 'font-family: Times New Roman;',
            'default': 'font-family: monospace, sans-serif;'
        };
        textAlign = {
            right: ['justify-content: flex-end;', 'text-align: right;'],
            start: ['justify-content: flex-start;', 'text-align: start;'],
            center: ['justify-content: center;', 'text-align: center;'],
            end: ['justify-content: flex-end;', 'text-align: end;'],
            left: ['justify-content: flex-start;', 'text-align: left;']
        };
        multiRowAlign = {
            start: 'text-align: start;',
            center: 'text-align: center;',
            end: 'text-align: end;',
            auto: ''
        };
        wrapOption = {
            wrap: 'white-space: normal;',
            noWrap: 'white-space: nowrap;'
        };
        unicodeBidi = {
            normal: 'unicode-bidi: normal;',
            embed: 'unicode-bidi: embed;',
            bidiOverride: 'unicode-bidi: bidi-override;'
        };
        displayAlign = {
            before: 'align-items: flex-start;',
            center: 'align-items: center;',
            after: 'align-items: flex-end;'
        };
        writingMode = {
            lrtb: '-webkit-writing-mode: horizontal-tb;' + 'writing-mode: horizontal-tb;',
            rltb: '-webkit-writing-mode: horizontal-tb;' + 'writing-mode: horizontal-tb;' + 'direction: rtl;' + 'unicode-bidi: bidi-override;',
            tbrl: '-webkit-writing-mode: vertical-rl;' + 'writing-mode: vertical-rl;' + '-webkit-text-orientation: upright;' + 'text-orientation: upright;',
            tblr: '-webkit-writing-mode: vertical-lr;' + 'writing-mode: vertical-lr;' + '-webkit-text-orientation: upright;' + 'text-orientation: upright;',
            lr: '-webkit-writing-mode: horizontal-tb;' + 'writing-mode: horizontal-tb;',
            rl: '-webkit-writing-mode: horizontal-tb;' + 'writing-mode: horizontal-tb;' + 'direction: rtl;',
            tb: '-webkit-writing-mode: vertical-rl;' + 'writing-mode: vertical-rl;' + '-webkit-text-orientation: upright;' + 'text-orientation: upright;'
        };
    }

    function parseTimings(timingStr) {
        // Test if the time provided by the caption is valid.
        var test = timingRegex.test(timingStr);
        var timeParts, parsedTime, frameRate;

        if (!test) {
            // Return NaN so it will throw an exception at internalParse if the time is incorrect.
            return NaN;
        }

        timeParts = timingStr.split(':');

        // Process the timings by decomposing it and converting it in numbers.
        parsedTime = parseFloat(timeParts[0]) * SECONDS_IN_HOUR + parseFloat(timeParts[1]) * SECONDS_IN_MIN + parseFloat(timeParts[2]);

        // In case a frameRate is provided, we adjust the parsed time.
        if (timeParts[3]) {
            frameRate = ttml.tt.frameRate;
            if (frameRate && !isNaN(frameRate)) {
                parsedTime += parseFloat(timeParts[3]) / frameRate;
            } else {
                return NaN;
            }
        }
        return parsedTime;
    }

    function getNamespacePrefix(json, ns) {
        // Obtain the namespace prefix.
        var r = Object.keys(json).filter(function (k) {
            return (k.split(':')[0] === 'xmlns' || k.split(':')[1] === 'xmlns') && json[k] === ns;
        }).map(function (k) {
            return k.split(':')[2] || k.split(':')[1];
        });
        if (r.length != 1) {
            return null;
        }
        return r[0];
    }

    function removeNamespacePrefix(json, nsPrefix) {
        for (var key in json) {
            if (json.hasOwnProperty(key)) {
                if ((typeof json[key] === 'object' || json[key] instanceof Object) && !Array.isArray(json[key])) {
                    removeNamespacePrefix(json[key], nsPrefix);
                } else if (Array.isArray(json[key])) {
                    for (var i = 0; i < json[key].length; i++) {
                        removeNamespacePrefix(json[key][i], nsPrefix);
                    }
                }
                var newKey = key.slice(key.indexOf(nsPrefix) + nsPrefix.length + 1);
                json[newKey] = json[key];
                delete json[key];
            }
        }
    }

    // backgroundColor = background-color, convert from camelCase to dash.
    function camelCaseToDash(key) {
        return key.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
    }

    // Convert an RGBA value written in Hex to rgba(v,v,v,a).
    function convertHexToRGBA(rgba) {
        // Get the hexadecimal value without the #.
        var hex = rgba.slice(1);
        // Separate the values in pairs.
        var hexMatrice = hex.match(/.{2}/g);
        // Convert the alpha value in decimal between 0 and 1.
        var alpha = parseFloat(parseInt(parseInt(hexMatrice[3], 16) / 255 * 1000, 10) / 1000);
        // Get the standard RGB value.
        var rgb = hexMatrice.slice(0, 3).map(function (i) {
            return parseInt(i, 16);
        });
        // Return the RGBA value for CSS.
        return 'rgba(' + rgb.join(',') + ',' + alpha + ');';
    }

    // Return whether or not an array contains a certain text
    function arrayContains(text, array) {
        for (var i = 0; i < array.length; i++) {
            if (array[i].indexOf(text) > -1) {
                return true;
            }
        }
        return false;
    }

    // Return the whole value that contains "text"
    function getPropertyFromArray(text, array) {
        for (var i = 0; i < array.length; i++) {
            if (array[i].indexOf(text) > -1) {
                return array[i];
            }
        }
        return null;
    }

    // Delete a a property from an array.
    function deletePropertyFromArray(property, array) {
        array.splice(array.indexOf(getPropertyFromArray(property, array)), 1);
    }

    function mergeArrays(primeArray, arrayToAdd) {
        for (var i = 0; i < primeArray.length; i++) {
            for (var j = 0; j < arrayToAdd.length; j++) {
                // Take only the name of the property
                if (primeArray[i]) {
                    if (primeArray[i].split(':')[0].indexOf(arrayToAdd[j].split(':')[0]) > -1) {
                        primeArray.splice(i, 1);
                    }
                }
            }
        }
        return primeArray.concat(arrayToAdd);
    }

    /**
     * Processing of styling information:
     * - processStyle: return an array of strings with the cue style under a CSS style form.
     * - findStyleFromID: Return the unprocessed style from TTMLStyling corresponding to the ID researched.
     * - getProcessedStyle: Return the processed style(s) from the ID(s) received in entry.
     * **/

    // Compute the style properties to return an array with the cleaned properties.
    function processStyle(cueStyle, cellUnit) {
        var properties = [];

        // Clean up from the xml2json parsing:
        for (var key in cueStyle) {
            if (cueStyle.hasOwnProperty(key)) {
                //Clean the properties from the parsing.
                var newKey = key.replace('ebutts:', '');
                newKey = newKey.replace('xml:', '');
                newKey = newKey.replace('tts:', '');

                // Clean the properties' names.
                newKey = camelCaseToDash(newKey);
                cueStyle[newKey] = cueStyle[key];
                delete cueStyle[key];
            }
        }

        // Line padding is computed from the cellResolution.
        if ('line-padding' in cueStyle) {
            var valuePadding = parseFloat(cueStyle['line-padding'].slice(cueStyle['line-padding'].indexOf(':') + 1, cueStyle['line-padding'].indexOf('c')));
            if ('id' in cueStyle) {
                linePadding[cueStyle.id] = valuePadding;
            }
            var valuePaddingInPx = valuePadding * cellUnit[0] + 'px;';
            properties.push('padding-left:' + valuePaddingInPx);
            properties.push('padding-right:' + valuePaddingInPx);
        }
        // Font size is computed from the cellResolution.
        if ('font-size' in cueStyle) {
            var valueFtSize = parseFloat(cueStyle['font-size'].slice(cueStyle['font-size'].indexOf(':') + 1, cueStyle['font-size'].indexOf('%')));
            if ('id' in cueStyle) {
                fontSize[cueStyle.id] = valueFtSize;
            }
            var valueFtSizeInPx = valueFtSize / 100 * cellUnit[1] + 'px;';
            properties.push('font-size:' + valueFtSizeInPx);
        }
        // Line height is computed from the cellResolution.
        if ('line-height' in cueStyle) {
            if (cueStyle['line-height'] === 'normal') {
                properties.push('line-height: normal;');
            } else {
                var valueLHSize = parseFloat(cueStyle['line-height'].slice(cueStyle['line-height'].indexOf(':') + 1, cueStyle['line-height'].indexOf('%')));
                if ('id' in cueStyle) {
                    lineHeight[cueStyle.id] = valueLHSize;
                }
                var valueLHSizeInPx = valueLHSize / 100 * cellUnit[1] + 'px;';
                properties.push('line-height' + ':' + valueLHSizeInPx);
            }
        }
        // Font-family can be specified by a generic family name or a custom family name.
        if ('font-family' in cueStyle) {
            if (cueStyle['font-family'] in fontFamilies) {
                properties.push(fontFamilies[cueStyle['font-family']]);
            } else {
                properties.push('font-family:' + cueStyle['font-family'] + ';');
            }
        }
        // Text align needs to be set from two properties:
        // The standard text-align CSS property.
        // The justify-content property as we use flex boxes.
        if ('text-align' in cueStyle) {
            if (cueStyle['text-align'] in textAlign) {
                properties.push(textAlign[cueStyle['text-align']][0]);
                properties.push(textAlign[cueStyle['text-align']][1]);
            }
        }
        // Multi Row align is set only by the text-align property.
        // TODO: TO CHECK
        if ('multi-row-align' in cueStyle) {
            if (arrayContains('text-align', properties) && cueStyle['multi-row-align'] != 'auto') {
                deletePropertyFromArray('text-align', properties);
            }
            if (cueStyle['multi-row-align'] in multiRowAlign) {
                properties.push(multiRowAlign[cueStyle['multi-row-align']]);
            }
        }
        // Background color can be specified from hexadecimal (RGB or RGBA) value.
        var rgbaValue;
        if ('background-color' in cueStyle) {
            if (cueStyle['background-color'].indexOf('#') > -1 && cueStyle['background-color'].length - 1 === 8) {
                rgbaValue = convertHexToRGBA(cueStyle['background-color']);
                properties.push('background-color: ' + rgbaValue);
            } else {
                properties.push('background-color:' + cueStyle['background-color'] + ';');
            }
        }
        // Color can be specified from hexadecimal (RGB or RGBA) value.
        if ('color' in cueStyle) {
            if (cueStyle.color.indexOf('#') > -1 && cueStyle.color.length - 1 === 8) {
                rgbaValue = convertHexToRGBA(cueStyle.color);
                properties.push('color: ' + rgbaValue);
            } else {
                properties.push('color:' + cueStyle.color + ';');
            }
        }
        // Wrap option is determined by the white-space CSS property.
        if ('wrap-option' in cueStyle) {
            if (cueStyle['wrap-option'] in wrapOption) {
                properties.push(wrapOption[cueStyle['wrap-option']]);
            } else {
                properties.push('white-space:' + cueStyle['wrap-option']);
            }
        }
        // Unicode bidi is determined by the unicode-bidi CSS property.
        if ('unicode-bidi' in cueStyle) {
            if (cueStyle['unicode-bidi'] in unicodeBidi) {
                properties.push(unicodeBidi[cueStyle['unicode-bidi']]);
            } else {
                properties.push('unicode-bidi:' + cueStyle['unicode-bidi']);
            }
        }

        // Standard properties identical to CSS.

        if ('font-style' in cueStyle) {
            properties.push('font-style:' + cueStyle['font-style'] + ';');
        }
        if ('font-weight' in cueStyle) {
            properties.push('font-weight:' + cueStyle['font-weight'] + ';');
        }
        if ('direction' in cueStyle) {
            properties.push('direction:' + cueStyle.direction + ';');
        }
        if ('text-decoration' in cueStyle) {
            properties.push('text-decoration:' + cueStyle['text-decoration'] + ';');
        }

        // Handle white-space preserve
        if (ttml.tt.hasOwnProperty('xml:space')) {
            if (ttml.tt['xml:space'] === 'preserve') {
                properties.push('white-space: pre;');
            }
        }

        return properties;
    }

    // Find the style set by comparing the style IDs available.
    // Return null if no style is found
    function findStyleFromID(ttmlStyling, cueStyleID) {
        // For every styles available, search the corresponding style in ttmlStyling.
        for (var j = 0; j < ttmlStyling.length; j++) {
            var currStyle = ttmlStyling[j];
            if (currStyle['xml:id'] === cueStyleID || currStyle.id === cueStyleID) {
                // Return the style corresponding to the ID in parameter.
                return currStyle;
            }
        }
        return null;
    }
    // Return the computed style from a certain ID.
    function getProcessedStyle(reference, cellUnit) {
        var styles = [];
        var ids = reference.match(/\S+/g);
        ids.forEach(function (id) {
            // Find the style for each id received.
            var cueStyle = findStyleFromID(ttmlStyling, id);
            if (cueStyle) {
                // Process the style for the cue in CSS form.
                // Send a copy of the style object, so it does not modify the original by cleaning it.
                var stylesFromId = processStyle(JSON.parse(JSON.stringify(cueStyle)), cellUnit);
                styles = styles.concat(stylesFromId);
            }
        });
        return styles;
    }

    /**
     * Processing of layout information:
     * - processRegion: return an array of strings with the cue region under a CSS style form.
     * - findRegionFromID: Return the unprocessed region from TTMLLayout corresponding to the ID researched.
     * - getProcessedRegion: Return the processed region(s) from the ID(s) received in entry.
     ***/

    // Compute the region properties to return an array with the cleaned properties.
    function processRegion(cueRegion, cellUnit) {
        var properties = [];

        // Clean up from the xml2json parsing:
        for (var key in cueRegion) {
            //Clean the properties from the parsing.
            var newKey = key.replace('tts:', '');
            newKey = newKey.replace('xml:', '');

            // Clean the properties' names.
            newKey = camelCaseToDash(newKey);
            cueRegion[newKey] = cueRegion[key];
            if (newKey !== key) {
                delete cueRegion[key];
            }
        }
        // Extent property corresponds to width and height
        if ('extent' in cueRegion) {
            var coordsExtent = cueRegion.extent.split(/\s/);
            properties.push('width: ' + coordsExtent[0] + ';');
            properties.push('height: ' + coordsExtent[1] + ';');
        }
        // Origin property corresponds to top and left
        if ('origin' in cueRegion) {
            var coordsOrigin = cueRegion.origin.split(/\s/);
            properties.push('left: ' + coordsOrigin[0] + ';');
            properties.push('top: ' + coordsOrigin[1] + ';');
        }
        // DisplayAlign property corresponds to vertical-align
        if ('display-align' in cueRegion) {
            properties.push(displayAlign[cueRegion['display-align']]);
        }
        // WritingMode is not yet implemented (for CSS3, to come)
        if ('writing-mode' in cueRegion) {
            properties.push(writingMode[cueRegion['writing-mode']]);
        }
        // Style will give to the region the style properties from the style selected
        if ('style' in cueRegion) {
            var styleFromID = getProcessedStyle(cueRegion.style, cellUnit);
            properties = properties.concat(styleFromID);
        }

        // Standard properties identical to CSS.

        if ('padding' in cueRegion) {
            properties.push('padding:' + cueRegion.padding + ';');
        }
        if ('overflow' in cueRegion) {
            properties.push('overflow:' + cueRegion.overflow + ';');
        }
        if ('show-background' in cueRegion) {
            properties.push('show-background:' + cueRegion['show-background'] + ';');
        }
        if ('id' in cueRegion) {
            properties.push('regionID:' + cueRegion.id + ';');
        }

        return properties;
    }

    // Find the region set by comparing the region IDs available.
    // Return null if no region is found
    function findRegionFromID(ttmlLayout, cueRegionID) {
        // For every region available, search the corresponding style in ttmlLayout.
        for (var j = 0; j < ttmlLayout.length; j++) {
            var currReg = ttmlLayout[j];
            if (currReg['xml:id'] === cueRegionID || currReg.id === cueRegionID) {
                // Return the region corresponding to the ID in parameter.
                return currReg;
            }
        }
        return null;
    }

    // Return the computed region from a certain ID.
    function getProcessedRegion(reference, cellUnit) {
        var regions = [];
        var ids = reference.match(/\S+/g);
        ids.forEach(function (id) {
            // Find the region for each id received.
            var cueRegion = findRegionFromID(ttmlLayout, id);
            if (cueRegion) {
                // Process the region for the cue in CSS form.
                // Send a copy of the style object, so it does not modify the original by cleaning it.
                var regionsFromId = processRegion(JSON.parse(JSON.stringify(cueRegion)), cellUnit);
                regions = regions.concat(regionsFromId);
            }
        });
        return regions;
    }

    //Return the cellResolution defined by the TTML document.
    function getCellResolution() {
        var defaultCellResolution = [32, 15]; // Default cellResolution.
        if (ttml.tt.hasOwnProperty('ttp:cellResolution')) {
            return ttml.tt['ttp:cellResolution'].split(' ').map(parseFloat);
        } else {
            return defaultCellResolution;
        }
    }

    // Return the cue wrapped into a span specifying its linePadding.
    function applyLinePadding(cueHTML, cueStyle) {
        // Extract the linePadding property from cueStyleProperties.
        var linePaddingLeft = getPropertyFromArray('padding-left', cueStyle);
        var linePaddingRight = getPropertyFromArray('padding-right', cueStyle);
        var linePadding = linePaddingLeft.concat(' ' + linePaddingRight + ' ');

        // Declaration of the HTML elements to be used in the cue innerHTML construction.
        var outerHTMLBeforeBr = '';
        var outerHTMLAfterBr = '';
        var cueInnerHTML = '';

        // List all the nodes of the subtitle.
        var nodeList = Array.prototype.slice.call(cueHTML.children);
        // Take a br element as reference.
        var brElement = cueHTML.getElementsByClassName('lineBreak')[0];
        // First index of the first br element.
        var idx = nodeList.indexOf(brElement);
        // array of all the br element indices
        var indices = [];
        // Find all the indices of the br elements.
        while (idx != -1) {
            indices.push(idx);
            idx = nodeList.indexOf(brElement, idx + 1);
        }

        // Strings for the cue innerHTML construction.
        var spanStringEnd = '<\/span>';
        var br = '<br>';
        var clonePropertyString = '<span' + ' class="spanPadding" ' + 'style="-webkit-box-decoration-break: clone; ';

        // If br elements are found:
        if (indices.length) {
            // For each index of a br element we compute the HTML coming before and/or after it.
            indices.forEach(function (i, index) {
                // If this is the first line break, we compute the HTML of the element coming before.
                if (index === 0) {
                    var styleBefore = '';
                    // for each element coming before the line break, we add its HTML.
                    for (var j = 0; j < i; j++) {
                        outerHTMLBeforeBr += nodeList[j].outerHTML;
                        // If this is the first element, we add its style to the wrapper.
                        if (j === 0) {
                            styleBefore = linePadding.concat(nodeList[j].style.cssText);
                        }
                    }
                    // The before element will comprises the clone property (for line wrapping), the style that
                    // need to be applied (ex: background-color) and the rest og the HTML.
                    outerHTMLBeforeBr = clonePropertyString + styleBefore + '">' + outerHTMLBeforeBr;
                }
                // For every element of the list, we compute the element coming after the line break.s
                var styleAfter = '';
                // for each element coming after the line break, we add its HTML.
                for (var k = i + 1; k < nodeList.length; k++) {
                    outerHTMLAfterBr += nodeList[k].outerHTML;
                    // If this is the last element, we add its style to the wrapper.
                    if (k === nodeList.length - 1) {
                        styleAfter += linePadding.concat(nodeList[k].style.cssText);
                    }
                }

                // The before element will comprises the clone property (for line wrapping), the style that
                // need to be applied (ex: background-color) and the rest og the HTML.
                outerHTMLAfterBr = clonePropertyString + styleAfter + '">' + outerHTMLAfterBr;

                // For each line break we must add the before and/or after element to the final cue as well as
                // the line break when needed.
                if (outerHTMLBeforeBr && outerHTMLAfterBr && index === indices.length - 1) {
                    cueInnerHTML += outerHTMLBeforeBr + spanStringEnd + br + outerHTMLAfterBr + spanStringEnd;
                } else if (outerHTMLBeforeBr && outerHTMLAfterBr && index !== indices.length - 1) {
                    cueInnerHTML += outerHTMLBeforeBr + spanStringEnd + br + outerHTMLAfterBr + spanStringEnd + br;
                } else if (outerHTMLBeforeBr && !outerHTMLAfterBr) {
                    cueInnerHTML += outerHTMLBeforeBr + spanStringEnd;
                } else if (!outerHTMLBeforeBr && outerHTMLAfterBr && index === indices.length - 1) {
                    cueInnerHTML += outerHTMLAfterBr + spanStringEnd;
                } else if (!outerHTMLBeforeBr && outerHTMLAfterBr && index !== indices.length - 1) {
                    cueInnerHTML += outerHTMLAfterBr + spanStringEnd + br;
                }
            });
        } else {
            // If there is no line break in the subtitle, we simply wrap cue in a span indicating the linePadding.
            var style = '';
            for (var k = 0; k < nodeList.length; k++) {
                style += nodeList[k].style.cssText;
            }
            cueInnerHTML = clonePropertyString + linePadding + style + '">' + cueHTML.innerHTML + spanStringEnd;
        }
        return cueInnerHTML;
    }

    /*** Create the cue element
     * I. The cues are text only:
     *      i) The cue contains a 'br' element
     *      ii) The cue contains a span element
     *      iii) The cue contains text
     * ***/

    function constructCue(cueElements, cellUnit) {
        var cue = document.createElement('div');
        cueElements.forEach(function (el) {
            // If metadata is present, do not process.
            if (el.hasOwnProperty('metadata')) {
                return;
            }

            /**
             * If the p element contains spans: create the span elements.
             */
            if (el.hasOwnProperty('span')) {

                // Stock the span subtitles in an array (in case there are only one value).
                var spanElements = el.span.__children;

                // Create the span element.
                var spanHTMLElement = document.createElement('span');
                // Extract the style of the span.
                if (el.span.hasOwnProperty('style')) {
                    var spanStyle = getProcessedStyle(el.span.style, cellUnit);
                    spanHTMLElement.className = 'spanPadding ' + el.span.style;
                    spanHTMLElement.style.cssText = spanStyle.join(' ');
                }

                // if the span has more than one element, we check for each of them their nature (br or text).
                spanElements.forEach(function (spanEl) {
                    // If metadata is present, do not process.
                    if (spanElements.hasOwnProperty('metadata')) {
                        return;
                    }
                    // If the element is a string
                    if (spanEl.hasOwnProperty('#text')) {
                        var textNode = document.createTextNode(spanEl['#text']);
                        spanHTMLElement.appendChild(textNode);
                        // If the element is a 'br' tag
                    } else if ('br' in spanEl) {
                            // To handle br inside span we need to add the current span
                            // to the cue and then create a br and add that the cue
                            // then create a new span that we use for the next line of
                            // text, that is a copy of the current span

                            // Add the current span to the cue, only if it has childNodes (text)
                            if (spanHTMLElement.hasChildNodes()) {
                                cue.appendChild(spanHTMLElement);
                            }

                            // Create a br and add that to the cue
                            var brEl = document.createElement('br');
                            brEl.className = 'lineBreak';
                            cue.appendChild(brEl);

                            // Create an replacement span and copy the style and classname from the old one
                            var newSpanHTMLElement = document.createElement('span');
                            newSpanHTMLElement.className = spanHTMLElement.className;
                            newSpanHTMLElement.style.cssText = spanHTMLElement.style.cssText;

                            // Replace the current span with the one we just created
                            spanHTMLElement = newSpanHTMLElement;
                        }
                });
                // We append the element to the cue container.
                cue.appendChild(spanHTMLElement);
            }

            /**
             * Create a br element if there is one in the cue.
             */
            else if (el.hasOwnProperty('br')) {
                    // We append the line break to the cue container.
                    var brEl = document.createElement('br');
                    brEl.className = 'lineBreak';
                    cue.appendChild(brEl);
                }

                /**
                 * Add the text that is not in any inline element
                 */
                else if (el.hasOwnProperty('#text')) {
                        // Add the text to an individual span element (to add line padding if it is defined).
                        var textNode = document.createElement('span');
                        textNode.textContent = el['#text'];

                        // We append the element to the cue container.
                        cue.appendChild(textNode);
                    }
        });
        return cue;
    }

    function constructCueRegion(cue, div, cellUnit) {
        var cueRegionProperties = []; // properties to be put in the "captionRegion" HTML element
        // Obtain the region ID(s) assigned to the cue.
        var pRegionID = cue.region;
        // If div has a region.
        var divRegionID = div.region;

        var divRegion;
        var pRegion;

        // If the div element reference a region.
        if (divRegionID) {
            divRegion = getProcessedRegion(divRegionID, cellUnit);
        }
        // If the p element reference a region.
        if (pRegionID) {
            pRegion = cueRegionProperties.concat(getProcessedRegion(pRegionID, cellUnit));
            if (divRegion) {
                cueRegionProperties = mergeArrays(divRegion, pRegion);
            } else {
                cueRegionProperties = pRegion;
            }
        } else if (divRegion) {
            cueRegionProperties = divRegion;
        }

        // Add initial/default values to what's not defined in the layout:
        applyDefaultProperties(cueRegionProperties, defaultLayoutProperties);

        return cueRegionProperties;
    }

    function constructCueStyle(cue, cellUnit) {
        var cueStyleProperties = []; // properties to be put in the "paragraph" HTML element
        // Obtain the style ID(s) assigned to the cue.
        var pStyleID = cue.style;
        // If body has a style.
        var bodyStyleID = ttml.tt.body.style;
        // If div has a style.
        var divStyleID = ttml.tt.body.div.style;

        var bodyStyle;
        var divStyle;
        var pStyle;
        var styleIDs = '';

        // If the body element reference a style.
        if (bodyStyleID) {
            bodyStyle = getProcessedStyle(bodyStyleID, cellUnit);
            styleIDs = 'paragraph ' + bodyStyleID;
        }

        // If the div element reference a style.
        if (divStyleID) {
            divStyle = getProcessedStyle(divStyleID, cellUnit);
            if (bodyStyle) {
                divStyle = mergeArrays(bodyStyle, divStyle);
                styleIDs += ' ' + divStyleID;
            } else {
                styleIDs = 'paragraph ' + divStyleID;
            }
        }

        // If the p element reference a style.
        if (pStyleID) {
            pStyle = getProcessedStyle(pStyleID, cellUnit);
            if (bodyStyle && divStyle) {
                cueStyleProperties = mergeArrays(divStyle, pStyle);
                styleIDs += ' ' + pStyleID;
            } else if (bodyStyle) {
                cueStyleProperties = mergeArrays(bodyStyle, pStyle);
                styleIDs += ' ' + pStyleID;
            } else if (divStyle) {
                cueStyleProperties = mergeArrays(divStyle, pStyle);
                styleIDs += ' ' + pStyleID;
            } else {
                cueStyleProperties = pStyle;
                styleIDs = 'paragraph ' + pStyleID;
            }
        } else if (bodyStyle && !divStyle) {
            cueStyleProperties = bodyStyle;
        } else if (!bodyStyle && divStyle) {
            cueStyleProperties = divStyle;
        }

        // Add initial/default values to what's not defined in the styling:
        applyDefaultProperties(cueStyleProperties, defaultStyleProperties);

        return [cueStyleProperties, styleIDs];
    }

    function applyDefaultProperties(array, defaultProperties) {
        for (var key in defaultProperties) {
            if (defaultProperties.hasOwnProperty(key)) {
                if (!arrayContains(key, array)) {
                    array.push(key + ':' + defaultProperties[key]);
                }
            }
        }
    }

    instance = {
        parse: parse,
        setConfig: setConfig
    };

    setup();
    return instance;
}
TTMLParser.__dashjs_factory_name = 'TTMLParser';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(TTMLParser);
module.exports = exports['default'];

},{"../../../externals/xml2json.js":3,"../../core/Debug.js":5,"../../core/FactoryMaker.js":7}],96:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

/**
 * @Module URLUtils
 * @description Provides utility functions for operating on URLs.
 * Initially this is simply a method to determine the Base URL of a URL, but
 * should probably include other things provided all over the place such as
 * determining whether a URL is relative/absolute, resolving two paths etc.
 */
function URLUtils() {

    var instance = undefined;

    var absUrl = /^(?:(?:[a-z]+:)?\/)?\//i;

    /**
     * Returns a string that contains the Base URL of a URL, if determinable.
     * @return {string}
     * @memberof module:URLUtils
     * @instance
     */
    function parseBaseUrl(url) {
        var base = '';

        if (url.indexOf('/') !== -1) {
            if (url.indexOf('?') !== -1) {
                url = url.substring(0, url.indexOf('?'));
            }
            base = url.substring(0, url.lastIndexOf('/') + 1);
        }

        return base;
    }

    /**
     * Determines whether the url is relative.
     * @return {bool}
     * @param {string} url
     * @memberof module:URLUtils
     * @instance
     */
    function isRelative(url) {
        return !absUrl.test(url);
    }

    instance = {
        parseBaseUrl: parseBaseUrl,
        isRelative: isRelative
    };

    return instance;
}

URLUtils.__dashjs_factory_name = 'URLUtils';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(URLUtils);
module.exports = exports['default'];

},{"../../core/FactoryMaker.js":7}],97:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
    value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _coreFactoryMakerJs = _dereq_('../../core/FactoryMaker.js');

var _coreFactoryMakerJs2 = _interopRequireDefault(_coreFactoryMakerJs);

var _coreDebugJs = _dereq_('../../core/Debug.js');

var _coreDebugJs2 = _interopRequireDefault(_coreDebugJs);

function VTTParser() {
    var context = this.context;
    var log = (0, _coreDebugJs2['default'])(context).getInstance().log;

    var instance = undefined,
        regExNewLine = undefined,
        regExToken = undefined,
        regExWhiteSpace = undefined,
        regExWhiteSpaceWordBoundary = undefined;

    function setup() {
        regExNewLine = /(?:\r\n|\r|\n)/gm;
        regExToken = /-->/;
        regExWhiteSpace = /(^[\s]+|[\s]+$)/g;
        regExWhiteSpaceWordBoundary = /\s\b/g;
    }

    function parse(data) {
        var captionArray = [];
        var len, lastStartTime;

        data = data.split(regExNewLine);
        len = data.length;
        lastStartTime = -1;

        for (var i = 0; i < len; i++) {
            var item = data[i];

            if (item.length > 0 && item !== 'WEBVTT') {
                if (item.match(regExToken)) {
                    var attributes = parseItemAttributes(item);
                    var cuePoints = attributes.cuePoints;
                    var styles = attributes.styles;
                    var text = getSublines(data, i + 1);
                    var startTime = convertCuePointTimes(cuePoints[0].replace(regExWhiteSpace, ''));
                    var endTime = convertCuePointTimes(cuePoints[1].replace(regExWhiteSpace, ''));

                    if (!isNaN(startTime) && !isNaN(endTime) && startTime >= lastStartTime && endTime > startTime) {
                        if (text !== '') {
                            lastStartTime = startTime;
                            //TODO Make VO external so other parsers can use.
                            captionArray.push({
                                start: startTime,
                                end: endTime,
                                data: text,
                                styles: styles
                            });
                        } else {
                            log('Skipping cue due to empty/malformed cue text');
                        }
                    } else {
                        log('Skipping cue due to incorrect cue timing');
                    }
                }
            }
        }

        return captionArray;
    }

    function convertCuePointTimes(time) {
        var timeArray = time.split(':');
        var len = timeArray.length - 1;

        time = parseInt(timeArray[len - 1], 10) * 60 + parseFloat(timeArray[len]);

        if (len === 2) {
            time += parseInt(timeArray[0], 10) * 3600;
        }

        return time;
    }

    function parseItemAttributes(data) {
        var vttCuePoints = data.split(regExToken);
        var arr = vttCuePoints[1].split(regExWhiteSpaceWordBoundary);
        arr.shift(); //remove first array index it is empty...
        vttCuePoints[1] = arr[0];
        arr.shift();
        return { cuePoints: vttCuePoints, styles: getCaptionStyles(arr) };
    }

    function getCaptionStyles(arr) {
        var styleObject = {};
        arr.forEach(function (element) {
            if (element.split(/:/).length > 1) {
                var val = element.split(/:/)[1];
                if (val && val.search(/%/) != -1) {
                    val = parseInt(val.replace(/%/, ''), 10);
                }
                if (element.match(/align/) || element.match(/A/)) {
                    styleObject.align = val;
                }
                if (element.match(/line/) || element.match(/L/)) {
                    styleObject.line = val;
                }
                if (element.match(/position/) || element.match(/P/)) {
                    styleObject.position = val;
                }
                if (element.match(/size/) || element.match(/S/)) {
                    styleObject.size = val;
                }
            }
        });

        return styleObject;
    }

    /**
        * VTT can have multiple lines to display per cuepoint.
        * */
    function getSublines(data, idx) {
        var i = idx;

        var subline = '';
        var lineData = '';
        var lineCount;

        while (data[i] !== '' && i < data.length) {
            i++;
        }

        lineCount = i - idx;
        if (lineCount > 1) {
            for (var j = 0; j < lineCount; j++) {
                lineData = data[idx + j];
                if (!lineData.match(regExToken)) {
                    subline += lineData;
                    if (j !== lineCount - 1) {
                        subline += '\n';
                    }
                } else {
                    // caption text should not have '-->' in it
                    subline = '';
                    break;
                }
            }
        } else {
            lineData = data[idx];
            if (!lineData.match(regExToken)) subline = lineData;
        }
        return decodeURI(subline);
    }

    instance = {
        parse: parse
    };

    setup();
    return instance;
}
VTTParser.__dashjs_factory_name = 'VTTParser';
exports['default'] = _coreFactoryMakerJs2['default'].getSingletonFactory(VTTParser);
module.exports = exports['default'];

},{"../../core/Debug.js":5,"../../core/FactoryMaker.js":7}],98:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var BitrateInfo = function BitrateInfo() {
  _classCallCheck(this, BitrateInfo);

  this.mediaType = null;
  this.bitrate = null;
  this.width = null;
  this.height = null;
  this.qualityIndex = NaN;
};

exports["default"] = BitrateInfo;
module.exports = exports["default"];

},{}],99:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var DataChunk =
//Represents a data structure that keep all the necessary info about a single init/media segment
function DataChunk() {
  _classCallCheck(this, DataChunk);

  this.streamId = null;
  this.mediaInfo = null;
  this.segmentType = null;
  this.quality = NaN;
  this.index = NaN;
  this.bytes = null;
  this.start = NaN;
  this.end = NaN;
  this.duration = NaN;
};

exports["default"] = DataChunk;
module.exports = exports["default"];

},{}],100:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Error = function Error(code, message, data) {
  _classCallCheck(this, Error);

  this.code = code || null;
  this.message = message || null;
  this.data = data || null;
};

exports["default"] = Error;
module.exports = exports["default"];

},{}],101:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var FragmentRequest = function FragmentRequest() {
  _classCallCheck(this, FragmentRequest);

  this.action = FragmentRequest.ACTION_DOWNLOAD;
  this.startTime = NaN;
  this.mediaType = null;
  this.mediaInfo = null;
  this.type = null;
  this.duration = NaN;
  this.timescale = NaN;
  this.range = null;
  this.url = null;
  this.serviceLocation = null;
  this.requestStartDate = null;
  this.firstByteDate = null;
  this.requestEndDate = null;
  this.quality = NaN;
  this.index = NaN;
  this.availabilityStartTime = null;
  this.availabilityEndTime = null;
  this.wallStartTime = null;
  this.bytesLoaded = NaN;
  this.bytesTotal = NaN;
  this.delayLoadingTime = NaN;
  this.responseType = 'arraybuffer';
};

FragmentRequest.ACTION_DOWNLOAD = 'download';
FragmentRequest.ACTION_COMPLETE = 'complete';

exports['default'] = FragmentRequest;
module.exports = exports['default'];

},{}],102:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _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); } } };

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _FragmentRequestJs = _dereq_('./FragmentRequest.js');

var _FragmentRequestJs2 = _interopRequireDefault(_FragmentRequestJs);

var HeadRequest = (function (_FragmentRequest) {
  _inherits(HeadRequest, _FragmentRequest);

  function HeadRequest(url) {
    _classCallCheck(this, HeadRequest);

    _get(Object.getPrototypeOf(HeadRequest.prototype), 'constructor', this).call(this);
    this.url = url || null;
    this.checkForExistenceOnly = true;
  }

  return HeadRequest;
})(_FragmentRequestJs2['default']);

exports['default'] = HeadRequest;
module.exports = exports['default'];

},{"./FragmentRequest.js":101}],103:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var IsoBox = function IsoBox() {
  _classCallCheck(this, IsoBox);

  this.offset = NaN;
  this.type = null;
  this.size = NaN;
  this.isComplete = true;
};

exports["default"] = IsoBox;
module.exports = exports["default"];

},{}],104:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var ManifestInfo = function ManifestInfo() {
  _classCallCheck(this, ManifestInfo);

  this.DVRWindowSize = NaN;
  this.loadedTime = null;
  this.availableFrom = null;
  this.minBufferTime = NaN;
  this.duration = NaN;
  this.isDynamic = false;
  this.maxFragmentDuration = null;
};

exports["default"] = ManifestInfo;
module.exports = exports["default"];

},{}],105:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var MediaInfo = function MediaInfo() {
  _classCallCheck(this, MediaInfo);

  this.id = null;
  this.index = null;
  this.type = null;
  this.streamInfo = null;
  this.representationCount = 0;
  this.lang = null;
  this.viewpoint = null;
  this.accessibility = null;
  this.audioChannelConfiguration = null;
  this.roles = null;
  this.codec = null;
  this.mimeType = null;
  this.contentProtection = null;
  this.isText = false;
  this.KID = null;
  this.bitrateList = null;
};

exports["default"] = MediaInfo;
module.exports = exports["default"];

},{}],106:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var MetricsList = function MetricsList() {
  _classCallCheck(this, MetricsList);

  this.TcpList = [];
  this.HttpList = [];
  this.RepSwitchList = [];
  this.BufferLevel = [];
  this.BufferState = [];
  this.PlayList = [];
  this.DroppedFrames = [];
  this.SchedulingInfo = [];
  this.DVRInfo = [];
  this.ManifestUpdate = [];
  this.RequestsQueue = null;
  this.DVBErrors = [];
  this.BolaState = [];
};

exports["default"] = MetricsList;
module.exports = exports["default"];

},{}],107:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var StreamInfo = function StreamInfo() {
  _classCallCheck(this, StreamInfo);

  this.id = null;
  this.index = null;
  this.start = NaN;
  this.duration = NaN;
  this.manifestInfo = null;
  this.isLast = true;
  this.isFirst = true;
};

exports["default"] = StreamInfo;
module.exports = exports["default"];

},{}],108:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _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); } } };

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _FragmentRequestJs = _dereq_('./FragmentRequest.js');

var _FragmentRequestJs2 = _interopRequireDefault(_FragmentRequestJs);

var TextRequest = (function (_FragmentRequest) {
  _inherits(TextRequest, _FragmentRequest);

  function TextRequest(url, type) {
    _classCallCheck(this, TextRequest);

    _get(Object.getPrototypeOf(TextRequest.prototype), 'constructor', this).call(this);
    this.url = url || null;
    this.type = type || null;
    this.mediaType = 'stream';
    this.responseType = 'text';
  }

  return TextRequest;
})(_FragmentRequestJs2['default']);

exports['default'] = TextRequest;
module.exports = exports['default'];

},{"./FragmentRequest.js":101}],109:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var TextTrackInfo = function TextTrackInfo() {
  _classCallCheck(this, TextTrackInfo);

  this.video = null;
  this.captionData = null;
  this.label = null;
  this.lang = null;
  this.defaultTrack = false;
  this.kind = null;
  this.isFragmented = false;
  this.isEmbedded = false;
};

exports["default"] = TextTrackInfo;
module.exports = exports["default"];

},{}],110:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var TrackInfo = function TrackInfo() {
  _classCallCheck(this, TrackInfo);

  this.id = null;
  this.quality = null;
  this.DVRWindow = null;
  this.fragmentDuration = null;
  this.mediaInfo = null;
  this.MSETimeOffset = null;
};

exports["default"] = TrackInfo;
module.exports = exports["default"];

},{}],111:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
    value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var URIFragmentData = function URIFragmentData() {
    _classCallCheck(this, URIFragmentData);

    this.t = null;
    this.xywh = null;
    this.track = null;
    this.id = null;
    this.s = null;
};

exports["default"] = URIFragmentData;

/*
    From Spec http://www.w3.org/TR/media-frags/

    temporal (t)     - This dimension denotes a specific time range in the original media, such as "starting at second 10, continuing until second 20";
    spatial  (xywh)  - this dimension denotes a specific range of pixels in the original media, such as "a rectangle with size (100,100) with its top-left at coordinate (10,10)";
                       Media fragments support also addressing the media along two additional dimensions (in the advanced version defined in Media Fragments 1.0 URI (advanced)):
    track    (track) - this dimension denotes one or more tracks in the original media, such as "the english audio and the video track";
    id       (id)    - this dimension denotes a named temporal fragment within the original media, such as "chapter 2", and can be seen as a convenient way of specifying a temporal fragment.


    ## Note
    Akamai is purposing to add #s=X to the ISO standard.
        - (X) Value would be a start time to seek to at startup instead of starting at 0 or live edge
        - Allows for seeking back before the start time unlike a temporal clipping.
*/
module.exports = exports["default"];

},{}],112:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2016, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 * @ignore
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var BolaState = function BolaState() {
  _classCallCheck(this, BolaState);

  /**
   * number
   * @private
   */
  this._s = undefined;
};

exports["default"] = BolaState;
module.exports = exports["default"];

},{}],113:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

/**
 * @class
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var BufferLevel =
/**
 * @description This Object holds reference to the current buffer level and the time it was recorded.
 */
function BufferLevel() {
  _classCallCheck(this, BufferLevel);

  /**
   * Real-Time | Time of the measurement of the buffer level.
   * @public
   */
  this.t = null;
  /**
   * Level of the buffer in milliseconds. Indicates the playout duration for which
   * media data of all active media components is available starting from the
   * current playout time.
   * @public
   */
  this.level = null;
};

exports["default"] = BufferLevel;
module.exports = exports["default"];

},{}],114:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var _controllersBufferControllerJs = _dereq_('../../controllers/BufferController.js');

var _controllersBufferControllerJs2 = _interopRequireDefault(_controllersBufferControllerJs);

/**
 * @class
 */

var BufferState =
/**
 * @description This Object holds reference to the current buffer state of the video element.
 */
function BufferState() {
  _classCallCheck(this, BufferState);

  /**
   * The Buffer Level Target determined by the BufferLevelRule.
   * @public
   */
  this.target = null;
  /**
   * Current buffer state. Will be BufferController.BUFFER_EMPTY or BufferController.BUFFER_LOADED.
   * @public
   */
  this.state = _controllersBufferControllerJs2['default'].BUFFER_EMPTY;
};

exports['default'] = BufferState;
module.exports = exports['default'];

},{"../../controllers/BufferController.js":49}],115:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var DVRInfo =
/**
 * @description This Object holds reference to DVR availability window information.
 */
function DVRInfo() {
  _classCallCheck(this, DVRInfo);

  /**
   * The current time of the video element when this was created.
   * @public
   */
  this.time = null;
  /**
   * The current Segment Availability Range as an object with start and end properties.
   * It's delta defined by the timeShiftBufferDepth MPD attribute.
   * @public
   */
  this.range = null;
  /**
   * Reference to the internal ManifestInfo.js VO.
   * @public
   */
  this.manifestInfo = null;
};

exports["default"] = DVRInfo;
module.exports = exports["default"];

},{}],116:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var DroppedFrames =
/**
 * @description This Object holds reference to DroppedFrames count and the time it was recorded.
 */
function DroppedFrames() {
  _classCallCheck(this, DroppedFrames);

  /**
   * Real-Time | Time of the measurement of the dropped frames.
   * @public
   */
  this.time = null;
  /**
   * Number of dropped frames
   * @public
   */
  this.droppedFrames = null;
};

exports["default"] = DroppedFrames;
module.exports = exports["default"];

},{}],117:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var HTTPRequest =
/**
 * @description This Object holds reference to the HTTPRequest for manifest, fragment and xlink loading.
 * Members which are not defined in ISO23009-1 Annex D should be prefixed by a _ so that they are ignored
 * by Metrics Reporting code.
 */
function HTTPRequest() {
  _classCallCheck(this, HTTPRequest);

  /**
   * Identifier of the TCP connection on which the HTTP request was sent.
   * @public
   */
  this.tcpid = null;
  /**
   * This is an optional parameter and should not be included in HTTP request/response transactions for progressive download.
   * The type of the request:
   * - MPD
   * - XLink expansion
   * - Initialization Fragment
   * - Index Fragment
   * - Media Fragment
   * - Bitstream Switching Fragment
   * - other
   * @public
   */
  this.type = null;
  /**
   * The original URL (before any redirects or failures)
   * @public
   */
  this.url = null;
  /**
   * The actual URL requested, if different from above
   * @public
   */
  this.actualurl = null;
  /**
   * The contents of the byte-range-spec part of the HTTP Range header.
   * @public
   */
  this.range = null;
  /**
   * Real-Time | The real time at which the request was sent.
   * @public
   */
  this.trequest = null;
  /**
   * Real-Time | The real time at which the first byte of the response was received.
   * @public
   */
  this.tresponse = null;
  /**
   * The HTTP response code.
   * @public
   */
  this.responsecode = null;
  /**
   * The duration of the throughput trace intervals (ms), for successful requests only.
   * @public
   */
  this.interval = null;
  /**
   * Throughput traces, for successful requests only.
   * @public
   */
  this.trace = [];

  /**
   * Type of stream ("audio" | "video" etc..)
   * @public
   */
  this._stream = null;
  /**
   * Real-Time | The real time at which the request finished.
   * @public
   */
  this._tfinish = null;
  /**
   * The duration of the media requests, if available, in milliseconds.
   * @public
   */
  this._mediaduration = null;
  /**
   * all the response headers from request.
   * @public
   */
  this._responseHeaders = null;
  /**
   * The selected service location for the request. string.
   * @public
   */
  this._serviceLocation = null;
};

HTTPRequest.Trace = (function () {
  /**
   * @description This Object holds reference to the progress of the HTTPRequest.
   */

  function _class() {
    _classCallCheck(this, _class);

    /**
     * Real-Time | Measurement stream start.
     * @public
     */
    this.s = null;
    /**
     * Measurement stream duration (ms).
     * @public
     */
    this.d = null;
    /**
     * List of integers counting the bytes received in each trace interval within the measurement stream.
     * @public
     */
    this.b = [];
  }

  return _class;
})();

HTTPRequest.MPD_TYPE = 'MPD';
HTTPRequest.XLINK_EXPANSION_TYPE = 'XLinkExpansion';
HTTPRequest.INIT_SEGMENT_TYPE = 'InitializationSegment';
HTTPRequest.INDEX_SEGMENT_TYPE = 'IndexSegment';
HTTPRequest.MEDIA_SEGMENT_TYPE = 'MediaSegment';
HTTPRequest.BITSTREAM_SWITCHING_SEGMENT_TYPE = 'BitstreamSwitchingSegment';
HTTPRequest.OTHER_TYPE = 'other';

exports['default'] = HTTPRequest;
module.exports = exports['default'];

},{}],118:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var ManifestUpdate =
/**
 * @description This Object holds reference to the manifest update information.
 */
function ManifestUpdate() {
  _classCallCheck(this, ManifestUpdate);

  /**
   * Media Type Video | Audio | FragmentedText
   * @public
   */
  this.mediaType = null;
  /**
   * MPD Type static | dynamic
   * @public
   */
  this.type = null;
  /**
   * When this manifest update was requested
   * @public
   */
  this.requestTime = null;
  /**
   * When this manifest update was received
   * @public
   */
  this.fetchTime = null;
  /**
   * Calculated Availability Start time of the stream.
   * @public
   */
  this.availabilityStartTime = null;
  /**
   * the seek point (liveEdge for dynamic, Stream[0].startTime for static)
   * @public
   */
  this.presentationStartTime = 0;
  /**
   * The calculated difference between the server and client wall clock time
   * @public
   */
  this.clientTimeOffset = 0;
  /**
   * Actual element.currentTime
   * @public
   */
  this.currentTime = null;
  /**
   * Actual element.ranges
   * @public
   */
  this.buffered = null;
  /**
   * Static is fixed value of zero. dynamic should be ((Now-@availabilityStartTime) - elementCurrentTime)
   * @public
   */
  this.latency = 0;
  /**
   * Array holding list of StreamInfo VO Objects
   * @public
   */
  this.streamInfo = [];
  /**
   * Array holding list of TrackInfo VO Objects
   * @public
   */
  this.trackInfo = [];
};

ManifestUpdate.StreamInfo = (function () {
  /**
   * @description This Object holds reference to the current period's stream information when the manifest was updated.
   */

  function _class() {
    _classCallCheck(this, _class);

    /**
     * Stream@id
     * @public
     */
    this.id = null;
    /**
     * Period Index
     * @public
     */
    this.index = null;
    /**
     * Stream@start
     * @public
     */
    this.start = null;
    /**
     * Stream@duration
     * @public
     */
    this.duration = null;
  }

  return _class;
})();

ManifestUpdate.TrackInfo = (function () {

  /**
   * @description This Object holds reference to the current representation's info when the manifest was updated.
   */

  function _class2() {
    _classCallCheck(this, _class2);

    /**
     * Track@id
     * @public
     */
    this.id = null;
    /**
     * Representation Index
     * @public
     */
    this.index = null;
    /**
     * Media Type Video | Audio | FragmentedText
     * @public
     */
    this.mediaType = null;
    /**
     * Which reprenset
     * @public
     */
    this.streamIndex = null;
    /**
     * Holds reference to @presentationTimeOffset
     * @public
     */
    this.presentationTimeOffset = null;
    /**
     * Holds reference to @startNumber
     * @public
     */
    this.startNumber = null;
    /**
     * list|template|timeline
     * @public
     */
    this.fragmentInfoType = null;
  }

  return _class2;
})();
//TODO we need exports for all the sub objects.  These should all be class VO
exports["default"] = ManifestUpdate;
module.exports = exports["default"];

},{}],119:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var PlayList =
/**
 * @description This Object holds reference to the playback session information
 */
function PlayList() {
  _classCallCheck(this, PlayList);

  /**
   * Timestamp of the user action that starts the playback stream...
   * @public
   */
  this.start = null;
  /**
   * Presentation time at which playout was requested by the user...
   * @public
   */
  this.mstart = null;
  /**
   * Type of user action which triggered playout
   * - New playout request (e.g. initial playout or seeking)
   * - Resume from pause
   * - Other user request (e.g. user-requested quality change)
   * - Start of a metrics collection stream (hence earlier entries in the play list not collected)
   * @public
   */
  this.starttype = null;

  /**
   * List of streams of continuous rendering of decoded samples.
   * @public
   */
  this.trace = [];
};

PlayList.Trace = (function () {
  function _class() {
    _classCallCheck(this, _class);

    /**
     * The value of the Representation@id of the Representation from which the samples were taken.
     * @type {String}
     * @public
     */
    this.representationid = null;
    /**
     * If not present, this metrics concerns the Representation as a whole.
     * If present, subreplevel indicates the greatest value of any
     * Subrepresentation@level being rendered.
     * @type {Number}
     * @public
     */
    this.subreplevel = null;
    /**
     * The time at which the first sample was rendered
     * @type {Number}
     * @public
     */
    this.start = null;
    /**
     * The presentation time of the first sample rendered.
     * @type {Number}
     * @public
     */
    this.mstart = null;
    /**
     * The duration of the continuously presented samples (which is the same in real time and media time). "Continuously presented" means that the media clock continued to advance at the playout speed throughout the interval. NOTE: the spec does not call out the units, but all other durations etc are in ms, and we use ms too.
     * @type {Number}
     * @public
     */
    this.duration = null;
    /**
     * The playback speed relative to normal playback speed (i.e.normal forward playback speed is 1.0).
     * @type {Number}
     * @public
     */
    this.playbackspeed = null;
    /**
     * The reason why continuous presentation of this Representation was stopped.
     * representation switch
     * rebuffering
     * user request
     * end of Period
     * end of Stream
     * end of content
     * end of a metrics collection period
     *
     * @type {String}
     * @public
     */
    this.stopreason = null;
  }

  return _class;
})();

/* Public Static Constants */
PlayList.INITIAL_PLAYOUT_START_REASON = 'initial_playout';
PlayList.SEEK_START_REASON = 'seek';
PlayList.RESUME_FROM_PAUSE_START_REASON = 'resume';
PlayList.METRICS_COLLECTION_START_REASON = 'metrics_collection_start';

PlayList.Trace.REPRESENTATION_SWITCH_STOP_REASON = 'representation_switch';
PlayList.Trace.REBUFFERING_REASON = 'rebuffering';
PlayList.Trace.USER_REQUEST_STOP_REASON = 'user_request';
PlayList.Trace.END_OF_PERIOD_STOP_REASON = 'end_of_period';
PlayList.Trace.END_OF_CONTENT_STOP_REASON = 'end_of_content';
PlayList.Trace.METRICS_COLLECTION_STOP_REASON = 'metrics_collection_end';
PlayList.Trace.FAILURE_STOP_REASON = 'failure';

exports['default'] = PlayList;
module.exports = exports['default'];

},{}],120:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var RepresentationSwitch =
/**
 * @description This Object holds reference to the info at quality switch between two representations.
 */
function RepresentationSwitch() {
  _classCallCheck(this, RepresentationSwitch);

  /**
   * Time of the switch event.
   * @public
   */
  this.t = null;
  /**
   * The media presentation time of the earliest access unit
   * (out of all media content components) played out from
   * the Representation.
   *
   * @public
   */
  this.mt = null;
  /**
   * Value of Representation@id identifying the switch-to Representation.
   * @public
   */
  this.to = null;
  /**
   * If not present, this metrics concerns the Representation as a whole.
   * If present, lto indicates the value of SubRepresentation@level within
   * Representation identifying the switch-to level of the Representation.
   *
   * @public
   */
  this.lto = null;
};

exports["default"] = RepresentationSwitch;
module.exports = exports["default"];

},{}],121:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var RequestsQueue =
/**
 * @description This Object holds reference to Fragment Model's request queues
 */
function RequestsQueue() {
  _classCallCheck(this, RequestsQueue);

  /**
   * Array of all of the requests that have begun to load
   * This request may not make it into the executed queue if it is abandon due to ABR rules for example.
   * @public
   */
  this.loadingRequests = [];
  /**
   * Array of the The requests that have completed
   * @public
   */
  this.executedRequests = [];
};

exports["default"] = RequestsQueue;
module.exports = exports["default"];

},{}],122:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var SchedulingInfo =
/**
 * @description This Object holds reference to the index handling of the current fragment being loaded or executed.
 */
function SchedulingInfo() {
  _classCallCheck(this, SchedulingInfo);

  /**
   * Type of stream Audio | Video | FragmentedText
   * @public
   */
  this.mediaType = null;
  /**
   * Time of the scheduling event.
   * @public
   */
  this.t = null;

  /**
   * Type of fragment (initialization | media)
   * @public
   */
  this.type = null;
  /**
   * Presentation start time of fragment
   * @public
   */
  this.startTime = null;
  /**
   * Availability start time of fragment
   * @public
   */
  this.availabilityStartTime = null;
  /**
   * Duration of fragment
   * @public
   */
  this.duration = null;
  /**
   * Bit Rate Quality of fragment
   * @public
   */
  this.quality = null;
  /**
   * Range of fragment
   * @public
   */
  this.range = null;

  /**
   * Current state of fragment
   * @public
   */
  this.state = null;
};

exports["default"] = SchedulingInfo;
module.exports = exports["default"];

},{}],123:[function(_dereq_,module,exports){
/**
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2013, Dash Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  * Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 * @class
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var TCPConnection =
/**
 * @description This Object holds reference to the current tcp connection
 */
function TCPConnection() {
  _classCallCheck(this, TCPConnection);

  /**
   * Identifier of the TCP connection on which the HTTP request was sent.
   * @public
   */
  this.tcpid = null;
  /**
   * IP Address of the interface over which the client is receiving the TCP data.
   * @public
   */
  this.dest = null;
  /**
   * Real-Time | The time at which the connection was opened (sending time of the initial SYN or connect socket operation).
   * @public
   */
  this.topen = null;
  /**
   * Real-Time | The time at which the connection was closed (sending or reception time of FIN or RST or close socket operation).
   * @public
   */
  this.tclose = null;
  /**
   * Connect time in ms (time from sending the initial SYN to receiving the ACK or completion of the connect socket operation).
   * @public
   */
  this.tconnect = null;
};

exports["default"] = TCPConnection;
module.exports = exports["default"];

},{}]},{},[37])(37)
});


}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{}],2:[function(_dereq_,module,exports){
'use strict';

exports.__esModule = true;
exports.default = cancelContentPlay;

var _window = _dereq_('global/window');

var _window2 = _interopRequireDefault(_window);

var _document = _dereq_('global/document');

var _document2 = _interopRequireDefault(_document);

var _video = _dereq_('video.js');

var _video2 = _interopRequireDefault(_video);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function cancelContentPlay(player) {
  if (player.ads.cancelPlayTimeout) {
    // another cancellation is already in flight, so do nothing
    return;
  }

  // Avoid content flash on non-iPad iOS and iPhones on iOS10 with playsinline
  if (_video2.default.browser.IS_IOS && _video2.default.browser.IS_IPHONE && !player.el_.hasAttribute('playsinline')) {

    var width = player.currentWidth ? player.currentWidth() : player.width();
    var height = player.currentHeight ? player.currentHeight() : player.height();

    // A placeholder black box will be shown in the document while the player is hidden.
    var placeholder = _document2.default.createElement('div');

    placeholder.style.width = width + 'px';
    placeholder.style.height = height + 'px';
    placeholder.style.background = 'black';
    player.el_.parentNode.insertBefore(placeholder, player.el_);

    // Hide the player. While in full-screen video playback mode on iOS, this
    // makes the player show a black screen instead of content flash.
    player.el_.style.display = 'none';

    // Unhide the player and remove the placeholder once we're ready to move on.
    player.one(['adstart', 'adplaying', 'adtimeout', 'adserror', 'adscanceled', 'adskip', 'playing'], function () {
      player.el_.style.display = 'block';
      placeholder.remove();
    });

    // Detect fullscreen change, if returning from fullscreen and placeholder exists,
    // remove placeholder and show player whether or not playsinline was attached.
    player.on('fullscreenchange', function () {
      if (placeholder && player.hasClass('vjs-fullscreen')) {
        player.el_.style.display = 'block';
        placeholder.remove();
      }
    });
  }

  // The timeout is necessary because pausing a video element while processing a `play`
  // event on iOS can cause the video element to continuously toggle between playing and
  // paused states.
  player.ads.cancelPlayTimeout = _window2.default.setTimeout(function () {
    // deregister the cancel timeout so subsequent cancels are scheduled
    player.ads.cancelPlayTimeout = null;

    // pause playback so ads can be handled.
    if (!player.paused()) {
      player.pause();
    }

    // When the 'content-playback' state is entered, this will let us know to play
    player.ads.cancelledPlay = true;
  }, 1);
} /*
  This feature makes sure the player is paused during ad loading.
  
  It does this by pausing the player immediately after a "play" where ads will be requested,
  then signalling that we should play after the ad is done.
  */
},{"global/document":176,"global/window":177,"video.js":390}],3:[function(_dereq_,module,exports){
'use strict';

exports.__esModule = true;
exports.default = initializeContentupdate;

var _window = _dereq_('global/window');

var _window2 = _interopRequireDefault(_window);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// Start sending contentupdate events
function initializeContentupdate(player) {

  // Keep track of the current content source
  // If you want to change the src of the video without triggering
  // the ad workflow to restart, you can update this variable before
  // modifying the player's source
  player.ads.contentSrc = player.currentSrc();

  // Check if a new src has been set, if so, trigger contentupdate
  var checkSrc = function checkSrc() {
    if (player.ads.state !== 'ad-playback') {
      var src = player.currentSrc();

      if (src !== player.ads.contentSrc) {
        player.trigger({
          type: 'contentupdate',
          oldValue: player.ads.contentSrc,
          newValue: src
        });
        player.ads.contentSrc = src;
      }
    }
  };

  // loadstart reliably indicates a new src has been set
  player.on('loadstart', checkSrc);
  // check immediately in case we missed the loadstart
  _window2.default.setTimeout(checkSrc, 1);
} /*
  This feature sends a `contentupdate` event when the player source changes.
  */
},{"global/window":177}],4:[function(_dereq_,module,exports){
'use strict';

exports.__esModule = true;
exports.processMetadataTracks = processMetadataTracks;
exports.setMetadataTrackMode = setMetadataTrackMode;
exports.getSupportedAdCue = getSupportedAdCue;
exports.getCueId = getCueId;
exports.processAdTrack = processAdTrack;

var _video = _dereq_('video.js');

var _video2 = _interopRequireDefault(_video);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
* This feature allows metadata text tracks to be manipulated once they are available,
* usually after the 'loadstart' event is observed on the player
* @param player A reference to a player
* @param processMetadataTrack A callback that performs some operations on a
* metadata text track
**/
function processMetadataTracks(player, processMetadataTrack) {
  var tracks = player.textTracks();
  var setModeAndProcess = function setModeAndProcess(track) {
    if (track.kind === 'metadata') {
      player.ads.cueTextTracks.setMetadataTrackMode(track);
      processMetadataTrack(player, track);
    }
  };

  // Text tracks are available
  if (tracks.length > 0) {
    for (var i = 0; i < tracks.length; i++) {
      var track = tracks[i];

      setModeAndProcess(track);
    }
    // Wait until text tracks are added
    // We avoid always setting the event handler in case
    // integrations decide to handle this separately
    // with a different handler for the same event
  } else {
    tracks.addEventListener('addtrack', function (event) {
      var track = event.track;

      setModeAndProcess(track);
    });
  }
}

/**
* Sets the track mode to one of 'disabled', 'hidden' or 'showing'
* @see https://github.com/videojs/video.js/blob/master/docs/guides/text-tracks.md
* Default behavior is to do nothing, @override if this is not desired
* @param track The text track to set the mode on
*/
/**
* This feature allows metadata text tracks to be manipulated once available
* @see processMetadataTracks.
* It also allows ad implementations to leverage ad cues coming through
* text tracks, @see processAdTrack
**/

function setMetadataTrackMode(track) {
  return;
}

/**
* Determines whether cue is an ad cue and returns the cue data.
* @param player A reference to the player
* @param cue The cue to be checked
* Returns the given cue by default @override if futher processing is required
* @return the cueData in JSON if cue is a supported ad cue, or -1 if not
**/
function getSupportedAdCue(player, cue) {
  return cue;
}

/**
* Gets the id associated with a cue.
* @param cue The cue to extract an ID from
* @returns The first occurance of 'id' in the object,
* @override if this is not the desired cue id
**/
function getCueId(player, cue) {
  return cue.id;
}

/**
* Checks whether a cue has already been used
* @param cueId The Id associated with a cue
**/
var cueAlreadySeen = function cueAlreadySeen(player, cueId) {
  return cueId !== undefined && player.ads.includedCues[cueId];
};

/**
* Indicates that a cue has been used
* @param cueId The Id associated with a cue
**/
var setCueAlreadySeen = function setCueAlreadySeen(player, cueId) {
  if (cueId !== undefined && cueId !== '') {
    player.ads.includedCues[cueId] = true;
  }
};

/**
* This feature allows ad metadata tracks to be manipulated in ad implementations
* @param player A reference to the player
* @param cues The set of cues to work with
* @param processCue A method that uses a cue to make some
* ad request in the ad implementation
* @param [cancelAds] A method that dynamically cancels ads in the ad implementation
**/
function processAdTrack(player, cues, processCue, cancelAds) {
  player.ads.includedCues = {};

  // loop over set of cues
  for (var i = 0; i < cues.length; i++) {
    var cue = cues[i];
    var cueData = this.getSupportedAdCue(player, cue);

    // Exit if this is not a supported cue
    if (cueData === -1) {
      _video2.default.log.warn('Skipping as this is not a supported ad cue.', cue);
      return;
    }

    // Continue processing supported cue
    var cueId = this.getCueId(player, cue);
    var startTime = cue.startTime;

    // Skip ad if cue was already used
    if (cueAlreadySeen(player, cueId)) {
      _video2.default.log('Skipping ad already seen with ID ' + cueId);
      return;
    }

    // Process cue as an ad cue
    processCue(player, cueData, cueId, startTime);

    // Indicate that this cue has been used
    setCueAlreadySeen(player, cueId);

    // Optional dynamic ad cancellation
    if (cancelAds !== undefined) {
      cancelAds(player, cueData);
    }
  }
}
},{"video.js":390}],5:[function(_dereq_,module,exports){
'use strict';

exports.__esModule = true;

var _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; }; /*
                                                                                                                                                                                                                                                                              This feature provides an optional method for ad integrations to insert run-time values
                                                                                                                                                                                                                                                                              into an ad server URL or configuration.
                                                                                                                                                                                                                                                                              */

exports.default = adMacroReplacement;

var _window = _dereq_('global/window');

var _window2 = _interopRequireDefault(_window);

var _document = _dereq_('global/document');

var _document2 = _interopRequireDefault(_document);

var _video = _dereq_('video.js');

var _video2 = _interopRequireDefault(_video);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// Return URI encoded version of value if uriEncode is true
var uriEncodeIfNeeded = function uriEncodeIfNeeded(value, uriEncode) {
  if (uriEncode) {
    return encodeURIComponent(value);
  }
  return value;
};

// Add custom field macros to macros object
// based on given name for custom fields property of mediainfo object.
var customFields = function customFields(mediainfo, macros, customFieldsName) {
  if (mediainfo && mediainfo[customFieldsName]) {
    var fields = mediainfo[customFieldsName];
    var fieldNames = Object.keys(fields);

    for (var i = 0; i < fieldNames.length; i++) {
      var tag = '{mediainfo.' + customFieldsName + '.' + fieldNames[i] + '}';

      macros[tag] = fields[fieldNames[i]];
    }
  }
};

// Public method that integrations use for ad macros.
// "string" is any string with macros to be replaced
// "uriEncode" if true will uri encode macro values when replaced
// "customMacros" is a object with custom macros and values to map them to
//  - For example: {'{five}': 5}
// Return value is is "string" with macros replaced
//  - For example: adMacroReplacement('{player.id}') returns a string of the player id
function adMacroReplacement(string, uriEncode, customMacros) {

  if (uriEncode === undefined) {
    uriEncode = false;
  }

  var macros = {};

  if (customMacros !== undefined) {
    macros = customMacros;
  }

  // Static macros
  macros['{player.id}'] = this.options_['data-player'];
  macros['{mediainfo.id}'] = this.mediainfo ? this.mediainfo.id : '';
  macros['{mediainfo.name}'] = this.mediainfo ? this.mediainfo.name : '';
  macros['{mediainfo.description}'] = this.mediainfo ? this.mediainfo.description : '';
  macros['{mediainfo.tags}'] = this.mediainfo ? this.mediainfo.tags : '';
  macros['{mediainfo.reference_id}'] = this.mediainfo ? this.mediainfo.reference_id : '';
  macros['{mediainfo.duration}'] = this.mediainfo ? this.mediainfo.duration : '';
  macros['{mediainfo.ad_keys}'] = this.mediainfo ? this.mediainfo.ad_keys : '';
  macros['{player.duration}'] = this.duration();
  macros['{timestamp}'] = new Date().getTime();
  macros['{document.referrer}'] = _document2.default.referrer;
  macros['{window.location.href}'] = _window2.default.location.href;
  macros['{random}'] = Math.floor(Math.random() * 1000000000000);

  // Custom fields in mediainfo
  customFields(this.mediainfo, macros, 'custom_fields');
  customFields(this.mediainfo, macros, 'customFields');

  // Go through all the replacement macros and apply them to the string.
  // This will replace all occurrences of the replacement macros.
  for (var i in macros) {
    string = string.split(i).join(uriEncodeIfNeeded(macros[i], uriEncode));
  }

  // Page variables
  string = string.replace(/{pageVariable\.([^}]+)}/g, function (match, name) {
    var value = void 0;
    var context = _window2.default;
    var names = name.split('.');

    // Iterate down multiple levels of selector without using eval
    // This makes things like pageVariable.foo.bar work
    for (var _i = 0; _i < names.length; _i++) {
      if (_i === names.length - 1) {
        value = context[names[_i]];
      } else {
        context = context[names[_i]];
      }
    }

    var type = typeof value === 'undefined' ? 'undefined' : _typeof(value);

    // Only allow certain types of values. Anything else is probably a mistake.
    if (value === null) {
      return 'null';
    } else if (value === undefined) {
      _video2.default.log.warn('Page variable "' + name + '" not found');
      return '';
    } else if (type !== 'string' && type !== 'number' && type !== 'boolean') {
      _video2.default.log.warn('Page variable "' + name + '" is not a supported type');
      return '';
    }

    return uriEncodeIfNeeded(String(value), uriEncode);
  });

  return string;
}
},{"global/document":176,"global/window":177,"video.js":390}],6:[function(_dereq_,module,exports){
'use strict';

var _window = _dereq_('global/window');

var _window2 = _interopRequireDefault(_window);

var _video = _dereq_('video.js');

var _video2 = _interopRequireDefault(_video);

var _redispatch = _dereq_('./redispatch.js');

var _redispatch2 = _interopRequireDefault(_redispatch);

var _snapshot = _dereq_('./snapshot.js');

var snapshot = _interopRequireWildcard(_snapshot);

var _contentupdate = _dereq_('./contentupdate.js');

var _contentupdate2 = _interopRequireDefault(_contentupdate);

var _cancelContentPlay = _dereq_('./cancelContentPlay.js');

var _cancelContentPlay2 = _interopRequireDefault(_cancelContentPlay);

var _macros = _dereq_('./macros.js');

var _macros2 = _interopRequireDefault(_macros);

var _cueTextTracks = _dereq_('./cueTextTracks.js');

var cueTextTracks = _interopRequireWildcard(_cueTextTracks);

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/*
This main plugin file is responsible for integration logic and enabling the features
that live in in separate files.
*/

var VIDEO_EVENTS = _video2.default.getComponent('Html5').Events;

/**
 * Remove the poster attribute from the video element tech, if present. When
 * reusing a video element for multiple videos, the poster image will briefly
 * reappear while the new source loads. Removing the attribute ahead of time
 * prevents the poster from showing up between videos.
 *
 * @param {Object} player The videojs player object
 */
var removeNativePoster = function removeNativePoster(player) {
  var tech = player.$('.vjs-tech');

  if (tech) {
    tech.removeAttribute('poster');
  }
};

// ---------------------------------------------------------------------------
// Ad Framework
// ---------------------------------------------------------------------------

// default framework settings
var defaults = {
  // maximum amount of time in ms to wait to receive `adsready` from the ad
  // implementation after play has been requested. Ad implementations are
  // expected to load any dynamic libraries and make any requests to determine
  // ad policies for a video during this time.
  timeout: 5000,

  // maximum amount of time in ms to wait for the ad implementation to start
  // linear ad mode after `readyforpreroll` has fired. This is in addition to
  // the standard timeout.
  prerollTimeout: 100,

  // maximum amount of time in ms to wait for the ad implementation to start
  // linear ad mode after `contentended` has fired.
  postrollTimeout: 100,

  // when truthy, instructs the plugin to output additional information about
  // plugin state to the video.js log. On most devices, the video.js log is
  // the same as the developer console.
  debug: false,

  // set this to true when using ads that are part of the content video
  stitchedAds: false
};

var contribAdsPlugin = function contribAdsPlugin(options) {

  var player = this; // eslint-disable-line consistent-this

  var settings = _video2.default.mergeOptions(defaults, options);

  // prefix all video element events during ad playback
  // if the video element emits ad-related events directly,
  // plugins that aren't ad-aware will break. prefixing allows
  // plugins that wish to handle ad events to do so while
  // avoiding the complexity for common usage
  var videoEvents = VIDEO_EVENTS.concat(['firstplay', 'loadedalldata', 'playing']);

  // Set up redispatching of player events
  player.on(videoEvents, _redispatch2.default);
  // Move redispatch to be first handler
  if (_video2.default.dom) {
    var data = _video2.default.dom.getElData(player.el());
    videoEvents.forEach(function (type) {
      var handlers = data.handlers[type];
      var handler = handlers && handlers.find(function (h) {
        return h.guid === _redispatch2.default.guid;
      });
      if (handler) {
        handlers.splice(handlers.indexOf(handler), 1);
        handlers.unshift(handler);
      }
    });
  }

  // videojs expects 'play' event but we redispatch it as 'contentplay'
  // so need to update play button state
  player.on('contentplay', function () {
    if (player.controlBar.playToggle) {
      player.controlBar.playToggle.handlePlay();
    }
  });

  // "vjs-has-started" should be present at the end of a video. This makes sure it's
  // always there.
  player.on('ended', function () {
    if (!player.hasClass('vjs-has-started')) {
      player.addClass('vjs-has-started');
    }
  });

  // We now auto-play when an ad gets loaded if we're playing ads in the same video
  // element as the content.
  // The problem is that in IE11, we cannot play in addurationchange but in iOS8, we
  // cannot play from adcanplay.
  // This will prevent ad-integrations from needing to do this themselves.
  player.on(['addurationchange', 'adcanplay'], function () {
    if (player.currentSrc() === player.ads.snapshot.currentSrc) {
      return;
    }

    player.play();
  });

  player.on('nopreroll', function () {
    player.ads.nopreroll_ = true;
  });

  player.on('nopostroll', function () {
    player.ads.nopostroll_ = true;
  });

  // Remove ad-loading class when ad plays or when content plays (in case there was no ad)
  // If you remove this class too soon you can get a flash of content!
  player.on(['ads-ad-started', 'playing'], function () {
    player.removeClass('vjs-ad-loading');
  });

  // Replace the plugin constructor with the ad namespace
  player.ads = {
    state: 'content-set',
    disableNextSnapshotRestore: false,

    // This is set to true if the content has ended once. After that, the user can
    // seek backwards and replay content, but _contentHasEnded remains true.
    _contentHasEnded: false,

    VERSION: '4.2.0-8',

    reset: function reset() {
      player.ads.disableNextSnapshotRestore = false;
      player.ads._contentHasEnded = false;
      player.ads.snapshot = null;
    },


    // Call this when an ad response has been received and there are
    // linear ads ready to be played.
    startLinearAdMode: function startLinearAdMode() {
      if (player.ads.state === 'preroll?' || player.ads.state === 'content-playback' || player.ads.state === 'postroll?') {
        player.trigger('adstart');
      }
    },


    // Call this when a linear ad pod has finished playing.
    endLinearAdMode: function endLinearAdMode() {
      if (player.ads.state === 'ad-playback' || player.ads.state === 'preroll?') {
        // In the case of an empty ad response, we want to make sure that
        // the vjs-ad-loading class is always removed. We could probably check for
        // duration on adPlayer for an empty ad but we remove it here just to make sure
        player.removeClass('vjs-ad-loading');
      }
      if (player.ads.state === 'ad-playback') {
        player.trigger('adend');
      }
      if (player.ads.state === 'preroll?') {
        player.trigger('nopreroll');
      }
    },


    // Call this when an ad response has been received but there are no
    // linear ads to be played (i.e. no ads available, or overlays).
    // This has no effect if we are already in a linear ad mode.  Always
    // use endLinearAdMode() to exit from linear ad-playback state.
    skipLinearAdMode: function skipLinearAdMode() {
      if (player.ads.state !== 'ad-playback') {
        player.trigger('adskip');
      }
    },
    stitchedAds: function stitchedAds(arg) {
      if (arg !== undefined) {
        this._stitchedAds = !!arg;
      }
      return this._stitchedAds;
    },


    // Returns whether the video element has been modified since the
    // snapshot was taken.
    // We test both src and currentSrc because changing the src attribute to a URL that
    // AdBlocker is intercepting doesn't update currentSrc.
    videoElementRecycled: function videoElementRecycled() {
      if (!this.snapshot) {
        throw new Error('You cannot use videoElementRecycled while there is no snapshot.');
      }

      var srcChanged = player.src() !== this.snapshot.src;
      var currentSrcChanged = player.currentSrc() !== this.snapshot.currentSrc;

      return srcChanged || currentSrcChanged;
    },


    // Returns a boolean indicating if given player is in live mode.
    // Can be replaced when this is fixed: https://github.com/videojs/video.js/issues/3262
    isLive: function isLive(somePlayer) {
      if (somePlayer.duration() === Infinity) {
        return true;
      } else if (_video2.default.browser.IOS_VERSION === '8' && somePlayer.duration() === 0) {
        return true;
      }
      return false;
    },


    // Return true if content playback should mute and continue during ad breaks.
    // This is only done during live streams on platforms where it's supported.
    // This improves speed and accuracy when returning from an ad break.
    shouldPlayContentBehindAd: function shouldPlayContentBehindAd(somePlayer) {
      return !settings.disablePlayContentBehindAd && !_video2.default.browser.IS_IOS && !_video2.default.browser.IS_ANDROID && somePlayer.duration() === Infinity;
    }
  };

  player.ads.stitchedAds(settings.stitchedAds);

  player.ads.cueTextTracks = cueTextTracks;
  player.ads.adMacroReplacement = _macros2.default.bind(player);

  // Start sending contentupdate events for this player
  (0, _contentupdate2.default)(player);

  // Global contentupdate handler for resetting plugin state
  player.on('contentupdate', player.ads.reset);

  // Ad Playback State Machine
  var states = {
    'content-set': {
      events: {
        adscanceled: function adscanceled() {
          this.state = 'content-playback';
        },
        adsready: function adsready() {
          this.state = 'ads-ready';
        },
        play: function play() {
          this.state = 'ads-ready?';
          (0, _cancelContentPlay2.default)(player);
          // remove the poster so it doesn't flash between videos
          removeNativePoster(player);
        },
        adserror: function adserror() {
          this.state = 'content-playback';
        },
        adskip: function adskip() {
          this.state = 'content-playback';
        }
      }
    },
    'ads-ready': {
      events: {
        play: function play() {
          this.state = 'preroll?';
          (0, _cancelContentPlay2.default)(player);
        },
        adskip: function adskip() {
          this.state = 'content-playback';
        },
        adserror: function adserror() {
          this.state = 'content-playback';
        }
      }
    },
    'preroll?': {
      enter: function enter() {
        if (player.ads.nopreroll_) {
          // This will start the ads manager in case there are later ads
          player.trigger('readyforpreroll');

          // If we don't wait a tick, entering content-playback will cancel
          // cancelPlayTimeout, causing the video to not pause for the ad
          _window2.default.setTimeout(function () {
            // Don't wait for a preroll
            player.trigger('nopreroll');
          }, 1);
        } else {
          // change class to show that we're waiting on ads
          player.addClass('vjs-ad-loading');
          // schedule an adtimeout event to fire if we waited too long
          player.ads.adTimeoutTimeout = _window2.default.setTimeout(function () {
            player.trigger('adtimeout');
          }, settings.prerollTimeout);
          // signal to ad plugin that it's their opportunity to play a preroll
          player.trigger('readyforpreroll');
        }
      },
      leave: function leave() {
        _window2.default.clearTimeout(player.ads.adTimeoutTimeout);
      },

      events: {
        play: function play() {
          (0, _cancelContentPlay2.default)(player);
        },
        adstart: function adstart() {
          this.state = 'ad-playback';
        },
        adskip: function adskip() {
          this.state = 'content-playback';
        },
        adtimeout: function adtimeout() {
          this.state = 'content-playback';
        },
        adserror: function adserror() {
          this.state = 'content-playback';
        },
        nopreroll: function nopreroll() {
          this.state = 'content-playback';
        }
      }
    },
    'ads-ready?': {
      enter: function enter() {
        player.addClass('vjs-ad-loading');
        player.ads.adTimeoutTimeout = _window2.default.setTimeout(function () {
          player.trigger('adtimeout');
        }, settings.timeout);
      },
      leave: function leave() {
        _window2.default.clearTimeout(player.ads.adTimeoutTimeout);
        player.removeClass('vjs-ad-loading');
      },

      events: {
        play: function play() {
          (0, _cancelContentPlay2.default)(player);
        },
        adscanceled: function adscanceled() {
          this.state = 'content-playback';
        },
        adsready: function adsready() {
          this.state = 'preroll?';
        },
        adskip: function adskip() {
          this.state = 'content-playback';
        },
        adtimeout: function adtimeout() {
          this.state = 'content-playback';
        },
        adserror: function adserror() {
          this.state = 'content-playback';
        }
      }
    },
    'ad-playback': {
      enter: function enter() {
        // capture current player state snapshot (playing, currentTime, src)
        if (!player.ads.shouldPlayContentBehindAd(player)) {
          this.snapshot = snapshot.getPlayerSnapshot(player);
        }

        // Mute the player behind the ad
        if (player.ads.shouldPlayContentBehindAd(player)) {
          this.preAdVolume_ = player.volume();
          player.volume(0);
        }

        // add css to the element to indicate and ad is playing.
        player.addClass('vjs-ad-playing');

        // We should remove the vjs-live class if it has been added in order to
        // show the adprogress control bar on Android devices for falsely
        // determined LIVE videos due to the duration incorrectly reported as Infinity
        if (player.hasClass('vjs-live')) {
          player.removeClass('vjs-live');
        }

        // remove the poster so it doesn't flash between ads
        removeNativePoster(player);

        // We no longer need to supress play events once an ad is playing.
        // Clear it if we were.
        if (player.ads.cancelPlayTimeout) {
          // If we don't wait a tick, we could cancel the pause for cancelContentPlay,
          // resulting in content playback behind the ad
          _window2.default.setTimeout(function () {
            _window2.default.clearTimeout(player.ads.cancelPlayTimeout);
            player.ads.cancelPlayTimeout = null;
          }, 1);
        }
      },
      leave: function leave() {
        player.removeClass('vjs-ad-playing');

        // We should add the vjs-live class back if the video is a LIVE video
        // If we dont do this, then for a LIVE Video, we will get an incorrect
        // styled control, which displays the time for the video
        if (player.ads.isLive(player)) {
          player.addClass('vjs-live');
        }
        if (!player.ads.shouldPlayContentBehindAd(player)) {
          snapshot.restorePlayerSnapshot(player, this.snapshot);
        }

        // Reset the volume to pre-ad levels
        if (player.ads.shouldPlayContentBehindAd(player)) {
          player.volume(this.preAdVolume_);
        }
      },

      events: {
        adend: function adend() {
          this.state = 'content-resuming';
        },
        adserror: function adserror() {
          this.state = 'content-resuming';
          // Trigger 'adend' to notify that we are exiting 'ad-playback'
          player.trigger('adend');
        }
      }
    },
    'content-resuming': {
      enter: function enter() {
        if (this._contentHasEnded) {
          _window2.default.clearTimeout(player.ads._fireEndedTimeout);
          // in some cases, ads are played in a swf or another video element
          // so we do not get an ended event in this state automatically.
          // If we don't get an ended event we can use, we need to trigger
          // one ourselves or else we won't actually ever end the current video.
          player.ads._fireEndedTimeout = _window2.default.setTimeout(function () {
            player.trigger('ended');
          }, 1000);
        }
      },
      leave: function leave() {
        _window2.default.clearTimeout(player.ads._fireEndedTimeout);
      },

      events: {
        contentupdate: function contentupdate() {
          this.state = 'content-set';
        },
        contentresumed: function contentresumed() {
          this.state = 'content-playback';
        },
        playing: function playing() {
          this.state = 'content-playback';
        },
        ended: function ended() {
          this.state = 'content-playback';
        }
      }
    },
    'postroll?': {
      enter: function enter() {
        this.snapshot = snapshot.getPlayerSnapshot(player);
        if (player.ads.nopostroll_) {
          _window2.default.setTimeout(function () {
            // content-resuming happens after the timeout for backward-compatibility
            // with plugins that relied on a postrollTimeout before nopostroll was
            // implemented
            player.ads.state = 'content-resuming';
            player.trigger('ended');
          }, 1);
        } else {
          player.addClass('vjs-ad-loading');

          player.ads.adTimeoutTimeout = _window2.default.setTimeout(function () {
            player.trigger('adtimeout');
          }, settings.postrollTimeout);
        }
      },
      leave: function leave() {
        _window2.default.clearTimeout(player.ads.adTimeoutTimeout);
        player.removeClass('vjs-ad-loading');
      },

      events: {
        adstart: function adstart() {
          this.state = 'ad-playback';
        },
        adskip: function adskip() {
          this.state = 'content-resuming';
          _window2.default.setTimeout(function () {
            player.trigger('ended');
          }, 1);
        },
        adtimeout: function adtimeout() {
          this.state = 'content-resuming';
          _window2.default.setTimeout(function () {
            player.trigger('ended');
          }, 1);
        },
        adserror: function adserror() {
          this.state = 'content-resuming';
          _window2.default.setTimeout(function () {
            player.trigger('ended');
          }, 1);
        },
        contentupdate: function contentupdate() {
          this.state = 'ads-ready?';
        }
      }
    },
    'content-playback': {
      enter: function enter() {
        // make sure that any cancelPlayTimeout is cleared
        if (player.ads.cancelPlayTimeout) {
          _window2.default.clearTimeout(player.ads.cancelPlayTimeout);
          player.ads.cancelPlayTimeout = null;
        }

        // This was removed because now that "playing" is fixed to only play after
        // preroll, any integration should just use the "playing" event. However,
        // we found out some 3rd party code relied on this event, so we've temporarily
        // added it back in to give people more time to update their code.
        player.trigger({
          type: 'contentplayback',
          triggerevent: player.ads.triggerevent
        });

        // Play the content
        if (player.ads.cancelledPlay) {
          player.ads.cancelledPlay = false;
          if (player.paused()) {
            player.play();
          }
        }
      },

      events: {
        // In the case of a timeout, adsready might come in late.
        // This assumes the behavior that if an ad times out, it could still
        // interrupt the content and start playing. An integration could
        // still decide to behave otherwise.
        adsready: function adsready() {
          player.trigger('readyforpreroll');
        },
        adstart: function adstart() {
          this.state = 'ad-playback';
        },
        contentupdate: function contentupdate() {
          // We know sources have changed, so we call CancelContentPlay
          // to avoid playback of video in the background of an ad. Playback Occurs on
          // Android devices if we do not call cancelContentPlay. This is because
          // the sources do not get updated in time on Android due to timing issues.
          // So instead of checking if the sources have changed in the play handler
          // and calling cancelContentPlay() there we call it here.
          // This does not happen on Desktop as the sources do get updated in time.
          if (!player.ads.shouldPlayContentBehindAd(player)) {
            (0, _cancelContentPlay2.default)(player);
          }
          if (player.paused()) {
            this.state = 'content-set';
          } else {
            this.state = 'ads-ready?';
          }
        },
        contentended: function contentended() {

          // If _contentHasEnded is true it means we already checked for postrolls and
          // played postrolls if needed, so now we're ready to send an ended event
          if (this._contentHasEnded) {
            // Causes ended event to trigger in content-resuming.enter.
            // From there, the ended event event is not redispatched.
            // Then we end up back in content-playback state.
            this.state = 'content-resuming';
            return;
          }

          this._contentHasEnded = true;
          this.state = 'postroll?';
        }
      }
    }
  };

  var processEvent = function processEvent(event) {

    var state = player.ads.state;

    // Execute the current state's handler for this event
    var eventHandlers = states[state].events;

    if (eventHandlers) {
      var handler = eventHandlers[event.type];

      if (handler) {
        handler.apply(player.ads);
      }
    }

    // If the state has changed...
    if (state !== player.ads.state) {
      var previousState = state;
      var newState = player.ads.state;

      // Record the event that caused the state transition
      player.ads.triggerevent = event.type;

      // Execute "leave" method for the previous state
      if (states[previousState].leave) {
        states[previousState].leave.apply(player.ads);
      }

      // Execute "enter" method for the new state
      if (states[newState].enter) {
        states[newState].enter.apply(player.ads);
      }

      // Debug log message for state changes
      if (settings.debug) {
        _video2.default.log('ads', player.ads.triggerevent + ' triggered: ' + previousState + ' -> ' + newState);
      }
    }
  };

  // Register our handler for the events that the state machine will process
  player.on(VIDEO_EVENTS.concat([
  // Events emitted by this plugin
  'adtimeout', 'contentupdate', 'contentplaying', 'contentended', 'contentresumed',
  // Triggered by startLinearAdMode()
  'adstart',
  // Triggered by endLinearAdMode()
  'adend',
  // Triggered by skipLinearAdMode()
  'adskip',

  // Events emitted by integrations
  'adsready', 'adserror', 'adscanceled', 'nopreroll']), processEvent);

  // If we're autoplaying, the state machine will immidiately process
  // a synthetic play event
  if (!player.paused()) {
    processEvent({ type: 'play' });
  }
};

// Register this plugin with videojs
_video2.default.plugin('ads', contribAdsPlugin);
},{"./cancelContentPlay.js":2,"./contentupdate.js":3,"./cueTextTracks.js":4,"./macros.js":5,"./redispatch.js":7,"./snapshot.js":8,"global/window":177,"video.js":390}],7:[function(_dereq_,module,exports){
'use strict';

exports.__esModule = true;
exports.default = redispatch;
/*
The goal of this feature is to make player events work as an integrator would
expect despite the presense of ads. For example, an integrator would expect
an `ended` event to happen once the content is ended. If an `ended` event is sent
as a result of an ad ending, that is a bug. The `redispatch` method should recognize
such `ended` events and prefix them so they are sent as `adended`, and so on with
all other player events.
*/

// Stop propogation for an event
var cancelEvent = function cancelEvent(player, event) {
  // Pretend we called stopImmediatePropagation because we want the native
  // element events to continue propagating
  event.isImmediatePropagationStopped = function () {
    return true;
  };
  event.cancelBubble = true;
  event.isPropagationStopped = function () {
    return true;
  };
};

// Stop propogation for an event, then send a new event with the type of the original
// event with the given prefix added.
var prefixEvent = function prefixEvent(player, prefix, event) {
  cancelEvent(player, event);
  player.trigger({
    type: prefix + event.type,
    state: player.ads.state,
    originalEvent: event
  });
};

// Handle a player event, either by redispatching it with a prefix, or by
// letting it go on its way without any meddling.
function redispatch(event) {

  // We do a quick play/pause before we check for prerolls. This creates a "playing"
  // event. This conditional block prefixes that event so it's "adplaying" if it
  // happens while we're in the "preroll?" state. Not every browser is in the
  // "preroll?" state for this event, so the following browsers come through here:
  //  * iPad
  //  * iPhone
  //  * Android
  //  * Safari
  // This is too soon to check videoElementRecycled because there is no snapshot
  // yet. We rely on the coincidence that all browsers for which
  // videoElementRecycled would be true also happen to send their initial playing
  // event during "preroll?"
  if (event.type === 'playing' && this.ads.state === 'preroll?') {
    prefixEvent(this, 'ad', event);

    // Here we send "adplaying" for browsers that send their initial "playing" event
    // (caused by the the initial play/pause) during the "ad-playback" state.
    // The following browsers come through here:
    // * Chrome
    // * IE11
    // If the ad plays in the content tech (aka videoElementRecycled) there will be
    // another playing event when the ad starts. We check videoElementRecycled to
    // avoid a second adplaying event. Thankfully, at this point a snapshot exists
    // so we can safely check videoElementRecycled.
  } else if (event.type === 'playing' && this.ads.state === 'ad-playback' && !this.ads.videoElementRecycled()) {
    prefixEvent(this, 'ad', event);

    // If the ad takes a long time to load, "playing" caused by play/pause can happen
    // during "ads-ready?" instead of "preroll?" or "ad-playback", skipping the
    // other conditions that would normally catch it
  } else if (event.type === 'playing' && this.ads.state === 'ads-ready?') {
    prefixEvent(this, 'ad', event);

    // When an ad is playing in content tech, we would normally prefix
    // "playing" with "ad" to send "adplaying". However, when we did a play/pause
    // before the preroll, we already sent "adplaying". This condition prevents us
    // from sending another.
  } else if (event.type === 'playing' && this.ads.state === 'ad-playback' && this.ads.videoElementRecycled()) {
    cancelEvent(this, event);
    return;

    // When ad is playing in content tech, prefix everything with "ad".
    // This block catches many events such as emptied, play, timeupdate, and ended.
  } else if (this.ads.state === 'ad-playback') {
    if (this.ads.videoElementRecycled() || this.ads.stitchedAds()) {
      prefixEvent(this, 'ad', event);
    }

    // Send contentended if ended happens during content.
    // We will make sure an ended event is sent after postrolls.
  } else if (this.ads.state === 'content-playback' && event.type === 'ended') {
    prefixEvent(this, 'content', event);

    // Event prefixing during content resuming is complicated
  } else if (this.ads.state === 'content-resuming') {

    // This does not happen during normal circumstances. I wasn't able to reproduce
    // it, but the working theory is that it handles cases where restoring the
    // snapshot takes a long time, such as in iOS7 and older Firefox.
    if (this.ads.snapshot && this.currentSrc() !== this.ads.snapshot.currentSrc) {

      // Don't prefix `loadstart` event
      if (event.type === 'loadstart') {
        return;
      }

      // All other events get "content" prefix
      return prefixEvent(this, 'content', event);

      // Content resuming after postroll
    } else if (this.ads.snapshot && this.ads.snapshot.ended) {

      // Don't prefix `pause` and `ended` events
      // They don't always happen during content-resuming, but they might.
      // It seems to happen most often on iOS and Android.
      if (event.type === 'pause' || event.type === 'ended') {
        return;
      }

      // All other events get "content" prefix
      return prefixEvent(this, 'content', event);
    }

    // Content resuming after preroll or midroll
    // Events besides "playing" and "error" get "content" prefix
    if (event.type !== 'playing' && event.type !== 'error') {
      prefixEvent(this, 'content', event);
    }
  }
}
},{}],8:[function(_dereq_,module,exports){
'use strict';

exports.__esModule = true;
exports.getPlayerSnapshot = getPlayerSnapshot;
exports.restorePlayerSnapshot = restorePlayerSnapshot;

var _window = _dereq_('global/window');

var _window2 = _interopRequireDefault(_window);

var _video = _dereq_('video.js');

var _video2 = _interopRequireDefault(_video);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/**
 * Returns an object that captures the portions of player state relevant to
 * video playback. The result of this function can be passed to
 * restorePlayerSnapshot with a player to return the player to the state it
 * was in when this function was invoked.
 * @param {Object} player The videojs player object
 */
/*
The snapshot feature is responsible for saving the player state before an ad, then
restoring the player state after an ad.
*/

function getPlayerSnapshot(player) {

  var currentTime = void 0;

  if (_video2.default.browser.IS_IOS && player.ads.isLive(player)) {
    // Record how far behind live we are
    if (player.seekable().length > 0) {
      currentTime = player.currentTime() - player.seekable().end(0);
    } else {
      currentTime = player.currentTime();
    }
  } else {
    currentTime = player.currentTime();
  }

  var tech = player.$('.vjs-tech');
  var remoteTracks = player.remoteTextTracks ? player.remoteTextTracks() : [];
  var tracks = player.textTracks ? player.textTracks() : [];
  var suppressedRemoteTracks = [];
  var suppressedTracks = [];
  var snapshotObject = {
    ended: player.ended(),
    currentSrc: player.currentSrc(),
    src: player.src(),
    currentTime: currentTime,
    type: player.currentType()
  };

  if (tech) {
    snapshotObject.nativePoster = tech.poster;
    snapshotObject.style = tech.getAttribute('style');
  }

  for (var i = 0; i < remoteTracks.length; i++) {
    var track = remoteTracks[i];

    suppressedRemoteTracks.push({
      track: track,
      mode: track.mode
    });
    track.mode = 'disabled';
  }
  snapshotObject.suppressedRemoteTracks = suppressedRemoteTracks;

  for (var _i = 0; _i < tracks.length; _i++) {
    var _track = tracks[_i];

    suppressedTracks.push({
      track: _track,
      mode: _track.mode
    });
    _track.mode = 'disabled';
  }
  snapshotObject.suppressedTracks = suppressedTracks;

  return snapshotObject;
}

/**
 * Attempts to modify the specified player so that its state is equivalent to
 * the state of the snapshot.
 * @param {Object} player - the videojs player object
 * @param {Object} snapshotObject - the player state to apply
 */
function restorePlayerSnapshot(player, snapshotObject) {

  if (player.ads.disableNextSnapshotRestore === true) {
    player.ads.disableNextSnapshotRestore = false;
    return;
  }

  // The playback tech
  var tech = player.$('.vjs-tech');

  // the number of[ remaining attempts to restore the snapshot
  var attempts = 20;

  var suppressedRemoteTracks = snapshotObject.suppressedRemoteTracks;
  var suppressedTracks = snapshotObject.suppressedTracks;
  var trackSnapshot = void 0;
  var restoreTracks = function restoreTracks() {
    for (var i = 0; i < suppressedRemoteTracks.length; i++) {
      trackSnapshot = suppressedRemoteTracks[i];
      trackSnapshot.track.mode = trackSnapshot.mode;
    }

    for (var _i2 = 0; _i2 < suppressedTracks.length; _i2++) {
      trackSnapshot = suppressedTracks[_i2];
      trackSnapshot.track.mode = trackSnapshot.mode;
    }
  };

  // finish restoring the playback state
  var resume = function resume() {
    var currentTime = void 0;

    if (_video2.default.browser.IS_IOS && player.ads.isLive(player)) {
      if (snapshotObject.currentTime < 0) {
        // Playback was behind real time, so seek backwards to match
        if (player.seekable().length > 0) {
          currentTime = player.seekable().end(0) + snapshotObject.currentTime;
        } else {
          currentTime = player.currentTime();
        }
        player.currentTime(currentTime);
      }
    } else if (snapshotObject.ended) {
      player.currentTime(player.duration());
    } else {
      player.currentTime(snapshotObject.currentTime);
    }

    // Resume playback if this wasn't a postroll
    if (!snapshotObject.ended) {
      player.play();
    }
  };

  // determine if the video element has loaded enough of the snapshot source
  // to be ready to apply the rest of the state
  var tryToResume = function tryToResume() {

    // tryToResume can either have been called through the `contentcanplay`
    // event or fired through setTimeout.
    // When tryToResume is called, we should make sure to clear out the other
    // way it could've been called by removing the listener and clearing out
    // the timeout.
    player.off('contentcanplay', tryToResume);
    if (player.ads.tryToResumeTimeout_) {
      player.clearTimeout(player.ads.tryToResumeTimeout_);
      player.ads.tryToResumeTimeout_ = null;
    }

    // Tech may have changed depending on the differences in sources of the
    // original video and that of the ad
    tech = player.el().querySelector('.vjs-tech');

    if (tech.readyState > 1) {
      // some browsers and media aren't "seekable".
      // readyState greater than 1 allows for seeking without exceptions
      return resume();
    }

    if (tech.seekable === undefined) {
      // if the tech doesn't expose the seekable time ranges, try to
      // resume playback immediately
      return resume();
    }

    if (tech.seekable.length > 0) {
      // if some period of the video is seekable, resume playback
      return resume();
    }

    // delay a bit and then check again unless we're out of attempts
    if (attempts--) {
      _window2.default.setTimeout(tryToResume, 50);
    } else {
      try {
        resume();
      } catch (e) {
        _video2.default.log.warn('Failed to resume the content after an advertisement', e);
      }
    }
  };

  if (snapshotObject.nativePoster) {
    tech.poster = snapshotObject.nativePoster;
  }

  if ('style' in snapshotObject) {
    // overwrite all css style properties to restore state precisely
    tech.setAttribute('style', snapshotObject.style || '');
  }

  // Determine whether the player needs to be restored to its state
  // before ad playback began. With a custom ad display or burned-in
  // ads, the content player state hasn't been modified and so no
  // restoration is required

  if (player.ads.videoElementRecycled()) {
    // on ios7, fiddling with textTracks too early will cause safari to crash
    player.one('contentloadedmetadata', restoreTracks);

    // if the src changed for ad playback, reset it
    player.src({ src: snapshotObject.currentSrc, type: snapshotObject.type });
    // safari requires a call to `load` to pick up a changed source
    player.load();
    // and then resume from the snapshots time once the original src has loaded
    // in some browsers (firefox) `canplay` may not fire correctly.
    // Reace the `canplay` event with a timeout.
    player.one('contentcanplay', tryToResume);
    player.ads.tryToResumeTimeout_ = player.setTimeout(tryToResume, 2000);
  } else if (!player.ended() || !snapshotObject.ended) {
    // if we didn't change the src, just restore the tracks
    restoreTracks();
    // the src didn't change and this wasn't a postroll
    // just resume playback at the current time.
    player.play();
  }
}
},{"global/window":177,"video.js":390}],9:[function(_dereq_,module,exports){
(function (global){
/**
 * @file add-text-track-data.js
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _holaOrgVideoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);

var _holaOrgVideoJs2 = _interopRequireDefault(_holaOrgVideoJs);

/**
 * Define properties on a cue for backwards compatability,
 * but warn the user that the way that they are using it
 * is depricated and will be removed at a later date.
 *
 * @param {Cue} cue the cue to add the properties on
 * @private
 */
var deprecateOldCue = function deprecateOldCue(cue) {
  Object.defineProperties(cue.frame, {
    id: {
      get: function get() {
        _holaOrgVideoJs2['default'].log.warn('cue.frame.id is deprecated. Use cue.value.key instead.');
        return cue.value.key;
      }
    },
    value: {
      get: function get() {
        _holaOrgVideoJs2['default'].log.warn('cue.frame.value is deprecated. Use cue.value.data instead.');
        return cue.value.data;
      }
    },
    privateData: {
      get: function get() {
        _holaOrgVideoJs2['default'].log.warn('cue.frame.privateData is deprecated. Use cue.value.data instead.');
        return cue.value.data;
      }
    }
  });
};

/**
 * Add text track data to a source handler given the captions and
 * metadata from the buffer.
 *
 * @param {Object} sourceHandler the flash or virtual source buffer
 * @param {Array} captionArray an array of caption data
 * @param {Array} cue an array of meta data
 * @private
 */
var addTextTrackData = function addTextTrackData(sourceHandler, captionArray, metadataArray) {
  var Cue = window.WebKitDataCue || window.VTTCue;

  if (captionArray) {
    captionArray.forEach(function (caption) {
      this.inbandTextTrack_.addCue(new Cue(caption.startTime + this.timestampOffset, caption.endTime + this.timestampOffset, caption.text));
    }, sourceHandler);
  }

  if (metadataArray) {
    metadataArray.forEach(function (metadata) {
      var time = metadata.cueTime + this.timestampOffset;

      metadata.frames.forEach(function (frame) {
        var cue = new Cue(time, time, frame.value || frame.url || frame.data || '');

        cue.frame = frame;
        cue.value = frame;
        deprecateOldCue(cue);
        this.metadataTrack_.addCue(cue);
      }, this);
    }, sourceHandler);
  }
};

exports['default'] = addTextTrackData;
module.exports = exports['default'];
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{}],10:[function(_dereq_,module,exports){
/**
 * @file codec-utils.js
 */

/**
 * Check if a codec string refers to an audio codec.
 *
 * @param {String} codec codec string to check
 * @return {Boolean} if this is an audio codec
 * @private
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});
var isAudioCodec = function isAudioCodec(codec) {
  return (/mp4a\.\d+.\d+/i.test(codec)
  );
};

/**
 * Check if a codec string refers to a video codec.
 *
 * @param {String} codec codec string to check
 * @return {Boolean} if this is a video codec
 * @private
 */
var isVideoCodec = function isVideoCodec(codec) {
  return (/avc1\.[\da-f]+/i.test(codec)
  );
};

/**
 * Parse a content type header into a type and parameters
 * object
 *
 * @param {String} type the content type header
 * @return {Object} the parsed content-type
 * @private
 */
var parseContentType = function parseContentType(type) {
  var object = { type: '', parameters: {} };
  var parameters = type.trim().split(';');

  // first parameter should always be content-type
  object.type = parameters.shift().trim();
  parameters.forEach(function (parameter) {
    var pair = parameter.trim().split('=');

    if (pair.length > 1) {
      var _name = pair[0].replace(/"/g, '').trim();
      var value = pair[1].replace(/"/g, '').trim();

      object.parameters[_name] = value;
    }
  });

  return object;
};

exports['default'] = {
  isAudioCodec: isAudioCodec,
  parseContentType: parseContentType,
  isVideoCodec: isVideoCodec
};
module.exports = exports['default'];
},{}],11:[function(_dereq_,module,exports){
/**
 * @file create-text-tracks-if-necessary.js
 */

/**
 * Create text tracks on video.js if they exist on a segment.
 *
 * @param {Object} sourceBuffer the VSB or FSB
 * @param {Object} mediaSource the HTML or Flash media source
 * @param {Object} segment the segment that may contain the text track
 * @private
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});
var createTextTracksIfNecessary = function createTextTracksIfNecessary(sourceBuffer, mediaSource, segment) {
  // create an in-band caption track if one is present in the segment
  if (segment.captions && segment.captions.length && !sourceBuffer.inbandTextTrack_) {
    sourceBuffer.inbandTextTrack_ = mediaSource.player_.addTextTrack('captions', 'cc1');
  }

  if (segment.metadata && segment.metadata.length && !sourceBuffer.metadataTrack_) {
    sourceBuffer.metadataTrack_ = mediaSource.player_.addTextTrack('metadata', 'Timed Metadata');
    sourceBuffer.metadataTrack_.inBandMetadataTrackDispatchType = segment.metadata.dispatchType;
  }
};

exports['default'] = createTextTracksIfNecessary;
module.exports = exports['default'];
},{}],12:[function(_dereq_,module,exports){
/**
 * @file flash-constants.js
 */
/**
 * The maximum size in bytes for append operations to the video.js
 * SWF. Calling through to Flash blocks and can be expensive so
 * we chunk data and pass through 4KB at a time, yielding to the
 * browser between chunks. This gives a theoretical maximum rate of
 * 1MB/s into Flash. Any higher and we begin to drop frames and UI
 * responsiveness suffers.
 *
 * @private
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
var flashConstants = {
  // times in milliseconds
  TIME_BETWEEN_CHUNKS: 1,
  BYTES_PER_CHUNK: 1024 * 32
};

exports["default"] = flashConstants;
module.exports = exports["default"];
},{}],13:[function(_dereq_,module,exports){
(function (global){
/**
 * @file flash-media-source.js
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _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; }; })();

var _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); } } };

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _holaOrgVideoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);

var _holaOrgVideoJs2 = _interopRequireDefault(_holaOrgVideoJs);

var _flashSourceBuffer = _dereq_('./flash-source-buffer');

var _flashSourceBuffer2 = _interopRequireDefault(_flashSourceBuffer);

var _flashConstants = _dereq_('./flash-constants');

var _flashConstants2 = _interopRequireDefault(_flashConstants);

var _codecUtils = _dereq_('./codec-utils');

/**
 * A flash implmentation of HTML MediaSources and a polyfill
 * for browsers that don't support native or HTML MediaSources..
 *
 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
 * @class FlashMediaSource
 * @extends videojs.EventTarget
 */

var FlashMediaSource = (function (_videojs$EventTarget) {
  _inherits(FlashMediaSource, _videojs$EventTarget);

  function FlashMediaSource() {
    var _this = this;

    _classCallCheck(this, FlashMediaSource);

    _get(Object.getPrototypeOf(FlashMediaSource.prototype), 'constructor', this).call(this);
    this.sourceBuffers = [];
    this.readyState = 'closed';

    this.on(['sourceopen', 'webkitsourceopen'], function (event) {
      // find the swf where we will push media data
      _this.swfObj = document.getElementById(event.swfId);
      _this.player_ = (0, _holaOrgVideoJs2['default'])(_this.swfObj.parentNode);
      _this.tech_ = _this.swfObj.tech;
      _this.readyState = 'open';

      _this.tech_.on('seeking', function () {
        var i = _this.sourceBuffers.length;

        while (i--) {
          _this.sourceBuffers[i].abort();
        }
      });

      // trigger load events
      if (_this.swfObj) {
        _this.swfObj.vjs_load();
      }
    });
  }

  /**
    * Set or return the presentation duration.
    *
    * @param {Double} value the duration of the media in seconds
    * @param {Double} the current presentation duration
    * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
    */

  /**
   * We have this function so that the html and flash interfaces
   * are the same.
   *
   * @private
   */

  _createClass(FlashMediaSource, [{
    key: 'addSeekableRange_',
    value: function addSeekableRange_() {}
    // intentional no-op

    /**
     * Create a new flash source buffer and add it to our flash media source.
     *
     * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
     * @param {String} type the content-type of the source
     * @return {Object} the flash source buffer
     */

  }, {
    key: 'addSourceBuffer',
    value: function addSourceBuffer(type) {
      var parsedType = (0, _codecUtils.parseContentType)(type);
      var sourceBuffer = undefined;

      // if this is an FLV type, we'll push data to flash
      if (parsedType.type === 'video/mp2t') {
        // Flash source buffers
        sourceBuffer = new _flashSourceBuffer2['default'](this);
      } else {
        throw new Error('NotSupportedError (Video.js)');
      }

      this.sourceBuffers.push(sourceBuffer);
      return sourceBuffer;
    }

    /**
     * Signals the end of the stream.
     *
     * @link https://w3c.github.io/media-source/#widl-MediaSource-endOfStream-void-EndOfStreamError-error
     * @param {String=} error Signals that a playback error
     * has occurred. If specified, it must be either "network" or
     * "decode".
     */
  }, {
    key: 'endOfStream',
    value: function endOfStream(error) {
      if (error === 'network') {
        // MEDIA_ERR_NETWORK
        this.tech_.error(2);
      } else if (error === 'decode') {
        // MEDIA_ERR_DECODE
        this.tech_.error(3);
      }
      if (this.readyState !== 'ended') {
        this.readyState = 'ended';
        this.swfObj.vjs_endOfStream();
      }
    }
  }]);

  return FlashMediaSource;
})(_holaOrgVideoJs2['default'].EventTarget);

exports['default'] = FlashMediaSource;
try {
  Object.defineProperty(FlashMediaSource.prototype, 'duration', {
    /**
     * Return the presentation duration.
     *
     * @return {Double} the duration of the media in seconds
     * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
     */
    get: function get() {
      if (!this.swfObj) {
        return NaN;
      }
      // get the current duration from the SWF
      return this.swfObj.vjs_getProperty('duration');
    },
    /**
     * Set the presentation duration.
     *
     * @param {Double} value the duration of the media in seconds
     * @return {Double} the duration of the media in seconds
     * @link http://www.w3.org/TR/media-source/#widl-MediaSource-duration
     */
    set: function set(value) {
      var i = undefined;
      var oldDuration = this.swfObj.vjs_getProperty('duration');

      this.swfObj.vjs_setProperty('duration', value);

      if (value < oldDuration) {
        // In MSE, this triggers the range removal algorithm which causes
        // an update to occur
        for (i = 0; i < this.sourceBuffers.length; i++) {
          this.sourceBuffers[i].remove(value, oldDuration);
        }
      }

      return value;
    }
  });
} catch (e) {
  // IE8 throws if defineProperty is called on a non-DOM node. We
  // don't support IE8 but we shouldn't throw an error if loaded
  // there.
  FlashMediaSource.prototype.duration = NaN;
}

for (var property in _flashConstants2['default']) {
  FlashMediaSource[property] = _flashConstants2['default'][property];
}
module.exports = exports['default'];
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{"./codec-utils":10,"./flash-constants":12,"./flash-source-buffer":14}],14:[function(_dereq_,module,exports){
(function (global){
/**
 * @file flash-source-buffer.js
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _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; }; })();

var _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); } } };

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _holaOrgVideoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);

var _holaOrgVideoJs2 = _interopRequireDefault(_holaOrgVideoJs);

var _holaOrgMuxJs = _dereq_('@hola.org/mux.js');

var _holaOrgMuxJs2 = _interopRequireDefault(_holaOrgMuxJs);

var _removeCuesFromTrack = _dereq_('./remove-cues-from-track');

var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);

var _createTextTracksIfNecessary = _dereq_('./create-text-tracks-if-necessary');

var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);

var _addTextTrackData = _dereq_('./add-text-track-data');

var _addTextTrackData2 = _interopRequireDefault(_addTextTrackData);

var _flashConstants = _dereq_('./flash-constants');

var _flashConstants2 = _interopRequireDefault(_flashConstants);

/**
 * A wrapper around the setTimeout function that uses
 * the flash constant time between ticks value.
 *
 * @param {Function} func the function callback to run
 * @private
 */
var scheduleTick = function scheduleTick(func) {
  // Chrome doesn't invoke requestAnimationFrame callbacks
  // in background tabs, so use setTimeout.
  window.setTimeout(func, _flashConstants2['default'].TIME_BETWEEN_CHUNKS);
};

/**
 * Generates a random string of max length 6
 *
 * @return {String} the randomly generated string
 * @function generateRandomString
 * @private
 */
var generateRandomString = function generateRandomString() {
  return Math.random().toString(36).slice(2, 8);
};

/**
 * Round a number to a specified number of places much like
 * toFixed but return a number instead of a string representation.
 *
 * @param {Number} num A number
 * @param {Number} places The number of decimal places which to
 * round
 * @private
 */
var toDecimalPlaces = function toDecimalPlaces(num, places) {
  if (typeof places !== 'number' || places < 0) {
    places = 0;
  }

  var scale = Math.pow(10, places);

  return Math.round(num * scale) / scale;
};

var byteArrayToString = function byteArrayToString(buffer) {
  var CHUNK_SIZE = 8 * 1024;
  var str = '';

  if (buffer.length <= CHUNK_SIZE) {
    return String.fromCharCode.apply(null, buffer);
  }
  for (var i = 0; i < buffer.length; i += CHUNK_SIZE) {
    str += String.fromCharCode.apply(null, buffer.subarray(i, i + CHUNK_SIZE));
  }
  return str;
};

/**
 * A SourceBuffer implementation for Flash rather than HTML.
 *
 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
 * @param {Object} mediaSource the flash media source
 * @class FlashSourceBuffer
 * @extends videojs.EventTarget
 */

var FlashSourceBuffer = (function (_videojs$EventTarget) {
  _inherits(FlashSourceBuffer, _videojs$EventTarget);

  function FlashSourceBuffer(mediaSource) {
    var _this = this;

    _classCallCheck(this, FlashSourceBuffer);

    _get(Object.getPrototypeOf(FlashSourceBuffer.prototype), 'constructor', this).call(this);
    var encodedHeader = undefined;

    // Start off using the globally defined value but refine
    // as we append data into flash
    this.chunkSize_ = _flashConstants2['default'].BYTES_PER_CHUNK;

    // byte arrays queued to be appended
    this.buffer_ = [];

    // the total number of queued bytes
    this.bufferSize_ = 0;

    // to be able to determine the correct position to seek to, we
    // need to retain information about the mapping between the
    // media timeline and PTS values
    this.basePtsOffset_ = NaN;

    this.mediaSource = mediaSource;

    // indicates whether the asynchronous continuation of an operation
    // is still being processed
    // see https://w3c.github.io/media-source/#widl-SourceBuffer-updating
    this.updating = false;
    this.timestampOffset_ = 0;

    // TS to FLV transmuxer
    this.segmentParser_ = new _holaOrgMuxJs2['default'].flv.Transmuxer();
    this.segmentParser_.on('data', this.receiveBuffer_.bind(this));
    encodedHeader = window.btoa(String.fromCharCode.apply(null, Array.prototype.slice.call(this.segmentParser_.getFlvHeader())));

    var safePlayerId = this.mediaSource.player_.id().replace(/[^a-zA-Z0-9]/g, '_');

    this.flashEncodedHeaderName_ = 'vjs_flashEncodedHeader_' + safePlayerId + generateRandomString();
    this.flashEncodedDataName_ = 'vjs_flashEncodedData_' + safePlayerId + generateRandomString();

    window[this.flashEncodedHeaderName_] = function () {
      delete window[_this.flashEncodedHeaderName_];
      return encodedHeader;
    };

    this.mediaSource.swfObj.vjs_appendChunkReady(this.flashEncodedHeaderName_);

    Object.defineProperty(this, 'timestampOffset', {
      get: function get() {
        return this.timestampOffset_;
      },
      set: function set(val) {
        if (typeof val === 'number' && val >= 0) {
          this.timestampOffset_ = val;
          this.segmentParser_ = new _holaOrgMuxJs2['default'].flv.Transmuxer();
          this.segmentParser_.on('data', this.receiveBuffer_.bind(this));
          // We have to tell flash to expect a discontinuity
          this.mediaSource.swfObj.vjs_discontinuity();
          // the media <-> PTS mapping must be re-established after
          // the discontinuity
          this.basePtsOffset_ = NaN;
        }
      }
    });

    Object.defineProperty(this, 'buffered', {
      get: function get() {
        if (!this.mediaSource || !this.mediaSource.swfObj || !('vjs_getProperty' in this.mediaSource.swfObj)) {
          return _holaOrgVideoJs2['default'].createTimeRange();
        }

        var buffered = this.mediaSource.swfObj.vjs_getProperty('buffered');

        if (buffered && buffered.length) {
          buffered[0][0] = toDecimalPlaces(buffered[0][0], 3);
          buffered[0][1] = toDecimalPlaces(buffered[0][1], 3);
        }
        return _holaOrgVideoJs2['default'].createTimeRanges(buffered);
      }
    });

    // On a seek we remove all text track data since flash has no concept
    // of a buffered-range and everything else is reset on seek
    this.mediaSource.player_.on('seeked', function () {
      (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.metadataTrack_);
      (0, _removeCuesFromTrack2['default'])(0, Infinity, _this.inbandTextTrack_);
    });
  }

  /**
   * Append bytes to the sourcebuffers buffer, in this case we
   * have to append it to swf object.
   *
   * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
   * @param {Array} bytes
   */

  _createClass(FlashSourceBuffer, [{
    key: 'appendBuffer',
    value: function appendBuffer(bytes) {
      var _this2 = this;

      var error = undefined;
      var chunk = 512 * 1024;
      var i = 0;

      if (this.updating) {
        error = new Error('SourceBuffer.append() cannot be called ' + 'while an update is in progress');
        error.name = 'InvalidStateError';
        error.code = 11;
        throw error;
      }

      this.updating = true;
      this.mediaSource.readyState = 'open';
      this.trigger({ type: 'update' });

      // this is here to use recursion
      var chunkInData = function chunkInData() {
        _this2.segmentParser_.push(bytes.subarray(i, i + chunk));
        i += chunk;
        if (i < bytes.byteLength) {
          scheduleTick(chunkInData);
        } else {
          scheduleTick(_this2.segmentParser_.flush.bind(_this2.segmentParser_));
        }
      };

      chunkInData();
    }

    /**
     * Reset the parser and remove any data queued to be sent to the SWF.
     *
     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
     */
  }, {
    key: 'abort',
    value: function abort() {
      this.buffer_ = [];
      this.bufferSize_ = 0;
      this.mediaSource.swfObj.vjs_abort();

      // report any outstanding updates have ended
      if (this.updating) {
        this.updating = false;
        this.trigger({ type: 'updateend' });
      }
    }

    /**
     * Flash cannot remove ranges already buffered in the NetStream
     * but seeking clears the buffer entirely. For most purposes,
     * having this operation act as a no-op is acceptable.
     *
     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
     * @param {Double} start start of the section to remove
     * @param {Double} end end of the section to remove
     */
  }, {
    key: 'remove',
    value: function remove(start, end) {
      (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);
      (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTrack_);
      this.trigger({ type: 'update' });
      this.trigger({ type: 'updateend' });
    }

    /**
     * Receive a buffer from the flv.
     *
     * @param {Object} segment
     * @private
     */
  }, {
    key: 'receiveBuffer_',
    value: function receiveBuffer_(segment) {
      var _this3 = this;

      // create an in-band caption track if one is present in the segment
      (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource, segment);
      (0, _addTextTrackData2['default'])(this, segment.captions, segment.metadata);

      // Do this asynchronously since convertTagsToData_ can be time consuming
      scheduleTick(function () {
        var flvBytes = _this3.convertTagsToData_(segment);

        if (_this3.buffer_.length === 0) {
          scheduleTick(_this3.processBuffer_.bind(_this3));
        }

        if (flvBytes) {
          _this3.buffer_.push(flvBytes);
          _this3.bufferSize_ += flvBytes.byteLength;
        }
      });
    }

    /**
     * Append a portion of the current buffer to the SWF.
     *
     * @private
     */
  }, {
    key: 'processBuffer_',
    value: function processBuffer_() {
      var _this4 = this;

      var chunkSize = _flashConstants2['default'].BYTES_PER_CHUNK;

      if (!this.buffer_.length) {
        if (this.updating !== false) {
          this.updating = false;
          this.trigger({ type: 'updateend' });
        }
        // do nothing if the buffer is empty
        return;
      }

      // concatenate appends up to the max append size
      var payload = new Uint8Array(Math.min(chunkSize, this.bufferSize_));
      var i = payload.byteLength;

      while (i) {
        var chunk = this.buffer_[0].subarray(0, i);

        payload.set(chunk, payload.byteLength - i);
        // requeue any bytes that won't make it this round
        if (chunk.byteLength < this.buffer_[0].byteLength) {
          this.buffer_[0] = this.buffer_[0].subarray(i);
        } else {
          this.buffer_.shift();
        }
        i -= chunk.byteLength;
      }
      this.bufferSize_ -= payload.byteLength;

      var b64str = window.btoa(byteArrayToString(payload));

      window[this.flashEncodedDataName_] = function () {
        // schedule another processBuffer to process any left over data or to
        // trigger updateend
        scheduleTick(_this4.processBuffer_.bind(_this4));
        delete window[_this4.flashEncodedDataName_];
        return b64str;
      };

      // Notify the swf that segment data is ready to be appended
      this.mediaSource.swfObj.vjs_appendChunkReady(this.flashEncodedDataName_);
    }

    /**
     * Turns an array of flv tags into a Uint8Array representing the
     * flv data. Also removes any tags that are before the current
     * time so that playback begins at or slightly after the right
     * place on a seek
     *
     * @private
     * @param {Object} segmentData object of segment data
     */
  }, {
    key: 'convertTagsToData_',
    value: function convertTagsToData_(segmentData) {
      var segmentByteLength = 0;
      var tech = this.mediaSource.tech_;
      var targetPts = 0;
      var i = undefined;
      var j = undefined;
      var segment = undefined;
      var filteredTags = [];
      var tags = this.getOrderedTags_(segmentData);

      // Establish the media timeline to PTS translation if we don't
      // have one already
      if (isNaN(this.basePtsOffset_) && tags.length) {
        this.basePtsOffset_ = tags[0].pts;
      }

      // Trim any tags that are before the end of the end of
      // the current buffer
      if (tech.buffered().length) {
        targetPts = tech.buffered().end(0) - this.timestampOffset;
      }
      // Trim to currentTime if it's ahead of buffered or buffered doesn't exist
      targetPts = Math.max(targetPts, tech.currentTime() - this.timestampOffset);

      // PTS values are represented in milliseconds
      targetPts *= 1e3;
      targetPts += this.basePtsOffset_;

      // skip tags with a presentation time less than the seek target
      for (i = 0; i < tags.length; i++) {
        if (tags[i].pts >= targetPts) {
          filteredTags.push(tags[i]);
        }
      }

      if (filteredTags.length === 0) {
        return;
      }

      // concatenate the bytes into a single segment
      for (i = 0; i < filteredTags.length; i++) {
        segmentByteLength += filteredTags[i].bytes.byteLength;
      }
      segment = new Uint8Array(segmentByteLength);
      for (i = 0, j = 0; i < filteredTags.length; i++) {
        segment.set(filteredTags[i].bytes, j);
        j += filteredTags[i].bytes.byteLength;
      }

      return segment;
    }

    /**
     * Assemble the FLV tags in decoder order.
     *
     * @private
     * @param {Object} segmentData object of segment data
     */
  }, {
    key: 'getOrderedTags_',
    value: function getOrderedTags_(segmentData) {
      var videoTags = segmentData.tags.videoTags;
      var audioTags = segmentData.tags.audioTags;
      var tag = undefined;
      var tags = [];

      while (videoTags.length || audioTags.length) {
        if (!videoTags.length) {
          // only audio tags remain
          tag = audioTags.shift();
        } else if (!audioTags.length) {
          // only video tags remain
          tag = videoTags.shift();
        } else if (audioTags[0].dts < videoTags[0].dts) {
          // audio should be decoded next
          tag = audioTags.shift();
        } else {
          // video should be decoded next
          tag = videoTags.shift();
        }

        tags.push(tag.finalize());
      }

      return tags;
    }
  }]);

  return FlashSourceBuffer;
})(_holaOrgVideoJs2['default'].EventTarget);

exports['default'] = FlashSourceBuffer;
module.exports = exports['default'];
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{"./add-text-track-data":9,"./create-text-tracks-if-necessary":11,"./flash-constants":12,"./remove-cues-from-track":16,"@hola.org/mux.js":27}],15:[function(_dereq_,module,exports){
(function (global){
/**
 * @file html-media-source.js
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _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; }; })();

var _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); } } };

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _holaOrgVideoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);

var _holaOrgVideoJs2 = _interopRequireDefault(_holaOrgVideoJs);

var _virtualSourceBuffer = _dereq_('./virtual-source-buffer');

var _virtualSourceBuffer2 = _interopRequireDefault(_virtualSourceBuffer);

var _codecUtils = _dereq_('./codec-utils');

/**
 * Replace the old apple-style `avc1.<dd>.<dd>` codec string with the standard
 * `avc1.<hhhhhh>`
 *
 * @param {Array} codecs an array of codec strings to fix
 * @return {Array} the translated codec array
 * @private
 */
var translateLegacyCodecs = function translateLegacyCodecs(codecs) {
  return codecs.map(function (codec) {
    return codec.replace(/avc1\.(\d+)\.(\d+)/i, function (orig, profile, avcLevel) {
      var profileHex = ('00' + Number(profile).toString(16)).slice(-2);
      var avcLevelHex = ('00' + Number(avcLevel).toString(16)).slice(-2);

      return 'avc1.' + profileHex + '00' + avcLevelHex;
    });
  });
};

/**
 * Our MediaSource implementation in HTML, mimics native
 * MediaSource where/if possible.
 *
 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
 * @class HtmlMediaSource
 * @extends videojs.EventTarget
 */

var HtmlMediaSource = (function (_videojs$EventTarget) {
  _inherits(HtmlMediaSource, _videojs$EventTarget);

  function HtmlMediaSource() {
    var _this = this;

    _classCallCheck(this, HtmlMediaSource);

    _get(Object.getPrototypeOf(HtmlMediaSource.prototype), 'constructor', this).call(this);
    var property = undefined;

    this.nativeMediaSource_ = new window.MediaSource();
    // delegate to the native MediaSource's methods by default
    for (property in this.nativeMediaSource_) {
      if (!(property in HtmlMediaSource.prototype) && typeof this.nativeMediaSource_[property] === 'function') {
        this[property] = this.nativeMediaSource_[property].bind(this.nativeMediaSource_);
      }
    }

    // emulate `duration` and `seekable` until seeking can be
    // handled uniformly for live streams
    // see https://github.com/w3c/media-source/issues/5
    this.duration_ = NaN;
    Object.defineProperty(this, 'duration', {
      get: function get() {
        if (this.duration_ === Infinity) {
          return this.duration_;
        }
        return this.nativeMediaSource_.duration;
      },
      set: function set(duration) {
        this.duration_ = duration;
        if (duration !== Infinity) {
          this.nativeMediaSource_.duration = duration;
          return;
        }
      }
    });
    Object.defineProperty(this, 'seekable', {
      get: function get() {
        if (this.duration_ === Infinity) {
          return _holaOrgVideoJs2['default'].createTimeRanges([[0, this.nativeMediaSource_.duration]]);
        }
        return this.nativeMediaSource_.seekable;
      }
    });

    Object.defineProperty(this, 'readyState', {
      get: function get() {
        return this.nativeMediaSource_.readyState;
      }
    });

    Object.defineProperty(this, 'activeSourceBuffers', {
      get: function get() {
        return this.activeSourceBuffers_;
      }
    });

    // the list of virtual and native SourceBuffers created by this
    // MediaSource
    this.sourceBuffers = [];

    this.activeSourceBuffers_ = [];

    /**
     * update the list of active source buffers based upon various
     * imformation from HLS and video.js
     *
     * @private
     */
    this.updateActiveSourceBuffers_ = function () {
      // Retain the reference but empty the array
      _this.activeSourceBuffers_.length = 0;

      // By default, the audio in the combined virtual source buffer is enabled
      // and the audio-only source buffer (if it exists) is disabled.
      var combined = false;
      var audioOnly = true;

      // TODO: maybe we can store the sourcebuffers on the track objects?
      // safari may do something like this
      for (var i = 0; i < _this.player_.audioTracks().length; i++) {
        var track = _this.player_.audioTracks()[i];

        if (track.enabled && track.kind !== 'main') {
          // The enabled track is an alternate audio track so disable the audio in
          // the combined source buffer and enable the audio-only source buffer.
          combined = true;
          audioOnly = false;
          break;
        }
      }

      // Since we currently support a max of two source buffers, add all of the source
      // buffers (in order).
      _this.sourceBuffers.forEach(function (sourceBuffer) {
        /* eslinst-disable */
        // TODO once codecs are required, we can switch to using the codecs to determine
        //      what stream is the video stream, rather than relying on videoTracks
        /* eslinst-enable */

        if (sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
          // combined
          sourceBuffer.audioDisabled_ = combined;
        } else if (sourceBuffer.videoCodec_ && !sourceBuffer.audioCodec_) {
          // If the "combined" source buffer is video only, then we do not want
          // disable the audio-only source buffer (this is mostly for demuxed
          // audio and video hls)
          sourceBuffer.audioDisabled_ = true;
          audioOnly = false;
        } else if (!sourceBuffer.videoCodec_ && sourceBuffer.audioCodec_) {
          // audio only
          sourceBuffer.audioDisabled_ = audioOnly;
          if (audioOnly) {
            return;
          }
        }

        _this.activeSourceBuffers_.push(sourceBuffer);
      });
    };

    // Re-emit MediaSource events on the polyfill
    ['sourceopen', 'sourceclose', 'sourceended'].forEach(function (eventName) {
      this.nativeMediaSource_.addEventListener(eventName, this.trigger.bind(this));
    }, this);

    // capture the associated player when the MediaSource is
    // successfully attached
    this.on('sourceopen', function (event) {
      // Get the player this MediaSource is attached to
      var video = document.querySelector('[src="' + _this.url_ + '"]');

      if (!video) {
        return;
      }

      _this.player_ = (0, _holaOrgVideoJs2['default'])(video.parentNode);

      _this.player_.audioTracks().on('change', _this.updateActiveSourceBuffers_);
      _this.player_.audioTracks().on('addtrack', _this.updateActiveSourceBuffers_);
      _this.player_.audioTracks().on('removetrack', _this.updateActiveSourceBuffers_);
    });

    // explicitly terminate any WebWorkers that were created
    // by SourceHandlers
    this.on('sourceclose', function (event) {
      this.sourceBuffers.forEach(function (sourceBuffer) {
        if (sourceBuffer.transmuxer_) {
          sourceBuffer.transmuxer_.terminate();
        }
      });
      this.sourceBuffers.length = 0;
      if (!this.player_) {
        return;
      }

      this.player_.audioTracks().off('change', this.updateActiveSourceBuffers_);
      this.player_.audioTracks().off('addtrack', this.updateActiveSourceBuffers_);
      this.player_.audioTracks().off('removetrack', this.updateActiveSourceBuffers_);
    });
  }

  /**
   * Add a range that that can now be seeked to.
   *
   * @param {Double} start where to start the addition
   * @param {Double} end where to end the addition
   * @private
   */

  _createClass(HtmlMediaSource, [{
    key: 'addSeekableRange_',
    value: function addSeekableRange_(start, end) {
      var error = undefined;

      if (this.duration !== Infinity) {
        error = new Error('MediaSource.addSeekableRange() can only be invoked ' + 'when the duration is Infinity');
        error.name = 'InvalidStateError';
        error.code = 11;
        throw error;
      }

      if (end > this.nativeMediaSource_.duration || isNaN(this.nativeMediaSource_.duration)) {
        this.nativeMediaSource_.duration = end;
      }
    }

    /**
     * Add a source buffer to the media source.
     *
     * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/addSourceBuffer
     * @param {String} type the content-type of the content
     * @return {Object} the created source buffer
     */
  }, {
    key: 'addSourceBuffer',
    value: function addSourceBuffer(type) {
      var buffer = undefined;
      var parsedType = (0, _codecUtils.parseContentType)(type);

      // Create a VirtualSourceBuffer to transmux MPEG-2 transport
      // stream segments into fragmented MP4s
      if (parsedType.type === 'video/mp2t') {
        var codecs = [];

        if (parsedType.parameters && parsedType.parameters.codecs) {
          codecs = parsedType.parameters.codecs.split(',');
          codecs = translateLegacyCodecs(codecs);
          // XXX alexeym: fix bug in firefox when audio codec begins from
          // whitespace character
          codecs = codecs.map(function (codec) {
            return codec.trim();
          });
          codecs = codecs.filter(function (codec) {
            return (0, _codecUtils.isAudioCodec)(codec) || (0, _codecUtils.isVideoCodec)(codec);
          });
        }

        if (codecs.length === 0) {
          codecs = ['avc1.4d400d', 'mp4a.40.2'];
        }

        buffer = new _virtualSourceBuffer2['default'](this, codecs);

        if (this.sourceBuffers.length !== 0) {
          // If another VirtualSourceBuffer already exists, then we are creating a
          // SourceBuffer for an alternate audio track and therefore we know that
          // the source has both an audio and video track.
          // That means we should trigger the manual creation of the real
          // SourceBuffers instead of waiting for the transmuxer to return data
          this.sourceBuffers[0].createRealSourceBuffers_();
          buffer.createRealSourceBuffers_();

          // Automatically disable the audio on the first source buffer if
          // a second source buffer is ever created
          this.sourceBuffers[0].audioDisabled_ = true;
        }
      } else {
        // delegate to the native implementation
        buffer = this.nativeMediaSource_.addSourceBuffer(type);
      }

      this.sourceBuffers.push(buffer);
      return buffer;
    }
  }]);

  return HtmlMediaSource;
})(_holaOrgVideoJs2['default'].EventTarget);

exports['default'] = HtmlMediaSource;
module.exports = exports['default'];
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{"./codec-utils":10,"./virtual-source-buffer":19}],16:[function(_dereq_,module,exports){
/**
 * @file remove-cues-from-track.js
 */

/**
 * Remove cues from a track on video.js.
 *
 * @param {Double} start start of where we should remove the cue
 * @param {Double} end end of where the we should remove the cue
 * @param {Object} track the text track to remove the cues from
 * @private
 */
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
var removeCuesFromTrack = function removeCuesFromTrack(start, end, track) {
  var i = undefined;
  var cue = undefined;

  if (!track) {
    return;
  }

  i = track.cues.length;

  while (i--) {
    cue = track.cues[i];

    // Remove any overlapping cue
    if (cue.startTime <= end && cue.endTime >= start) {
      track.removeCue(cue);
    }
  }
};

exports["default"] = removeCuesFromTrack;
module.exports = exports["default"];
},{}],17:[function(_dereq_,module,exports){
/**
 * @file transmuxer-worker.js
 */

/**
 * videojs-contrib-media-sources
 *
 * Copyright (c) 2015 Brightcove
 * All rights reserved.
 *
 * Handles communication between the browser-world and the mux.js
 * transmuxer running inside of a WebWorker by exposing a simple
 * message-based interface to a Transmuxer object.
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _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; }; })();

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var _holaOrgMuxJs = _dereq_('@hola.org/mux.js');

var _holaOrgMuxJs2 = _interopRequireDefault(_holaOrgMuxJs);

/**
 * Re-emits tranmsuxer events by converting them into messages to the
 * world outside the worker.
 *
 * @param {Object} transmuxer the transmuxer to wire events on
 * @private
 */
var wireTransmuxerEvents = function wireTransmuxerEvents(transmuxer) {
  transmuxer.on('data', function (segment) {
    // transfer ownership of the underlying ArrayBuffer
    // instead of doing a copy to save memory
    // ArrayBuffers are transferable but generic TypedArrays are not
    // @link https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)
    var typedArray = segment.data;

    segment.data = typedArray.buffer;
    postMessage({
      action: 'data',
      segment: segment,
      byteOffset: typedArray.byteOffset,
      byteLength: typedArray.byteLength
    }, [segment.data]);
  });

  if (transmuxer.captionStream) {
    transmuxer.captionStream.on('data', function (caption) {
      postMessage({
        action: 'caption',
        data: caption
      });
    });
  }

  transmuxer.on('done', function (data) {
    postMessage({ action: 'done' });
  });
};

/**
 * All incoming messages route through this hash. If no function exists
 * to handle an incoming message, then we ignore the message.
 *
 * @class MessageHandlers
 * @param {Object} options the options to initialize with
 */

var MessageHandlers = (function () {
  function MessageHandlers(options) {
    _classCallCheck(this, MessageHandlers);

    this.options = options || {};
    this.init();
  }

  /**
   * Our web wroker interface so that things can talk to mux.js
   * that will be running in a web worker. the scope is passed to this by
   * webworkify.
   *
   * @param {Object} self the scope for the web worker
   */

  /**
   * initialize our web worker and wire all the events.
   */

  _createClass(MessageHandlers, [{
    key: 'init',
    value: function init() {
      if (this.transmuxer) {
        this.transmuxer.dispose();
      }
      this.transmuxer = new _holaOrgMuxJs2['default'].mp4.Transmuxer(this.options);
      wireTransmuxerEvents(this.transmuxer);
    }

    /**
     * Adds data (a ts segment) to the start of the transmuxer pipeline for
     * processing.
     *
     * @param {ArrayBuffer} data data to push into the muxer
     */
  }, {
    key: 'push',
    value: function push(data) {
      // Cast array buffer to correct type for transmuxer
      var segment = new Uint8Array(data.data, data.byteOffset, data.byteLength);

      this.transmuxer.push(segment);
    }

    /**
     * Recreate the transmuxer so that the next segment added via `push`
     * start with a fresh transmuxer.
     */
  }, {
    key: 'reset',
    value: function reset() {
      this.init();
    }

    /**
     * Set the value that will be used as the `baseMediaDecodeTime` time for the
     * next segment pushed in. Subsequent segments will have their `baseMediaDecodeTime`
     * set relative to the first based on the PTS values.
     *
     * @param {Object} data used to set the timestamp offset in the muxer
     */
  }, {
    key: 'setTimestampOffset',
    value: function setTimestampOffset(data) {
      var timestampOffset = data.timestampOffset || 0;

      this.transmuxer.setBaseMediaDecodeTime(Math.round(timestampOffset * 90000));
    }

    /**
     * Forces the pipeline to finish processing the last segment and emit it's
     * results.
     *
     * @param {Object} data event data, not really used
     */
  }, {
    key: 'flush',
    value: function flush(data) {
      this.transmuxer.flush();
    }
  }]);

  return MessageHandlers;
})();

var Worker = function Worker(self) {
  self.onmessage = function (event) {
    if (event.data.action === 'init' && event.data.options) {
      this.messageHandlers = new MessageHandlers(event.data.options);
      return;
    }

    if (!this.messageHandlers) {
      this.messageHandlers = new MessageHandlers();
    }

    if (event.data && event.data.action && event.data.action !== 'init') {
      if (this.messageHandlers[event.data.action]) {
        this.messageHandlers[event.data.action](event.data);
      }
    }
  };
};

exports['default'] = function (self) {
  return new Worker(self);
};

module.exports = exports['default'];
},{"@hola.org/mux.js":27}],18:[function(_dereq_,module,exports){
(function (global){
/**
 * @file videojs-contrib-media-sources.js
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _flashMediaSource = _dereq_('./flash-media-source');

var _flashMediaSource2 = _interopRequireDefault(_flashMediaSource);

var _htmlMediaSource = _dereq_('./html-media-source');

var _htmlMediaSource2 = _interopRequireDefault(_htmlMediaSource);

var _holaOrgVideoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);

var _holaOrgVideoJs2 = _interopRequireDefault(_holaOrgVideoJs);

var urlCount = 0;

// ------------
// Media Source
// ------------

var defaults = {
  // how to determine the MediaSource implementation to use. There
  // are three available modes:
  // - auto: use native MediaSources where available and Flash
  //   everywhere else
  // - html5: always use native MediaSources
  // - flash: always use the Flash MediaSource polyfill
  mode: 'auto'
};

// store references to the media sources so they can be connected
// to a video element (a swf object)
// TODO: can we store this somewhere local to this module?
_holaOrgVideoJs2['default'].mediaSources = {};

/**
 * Provide a method for a swf object to notify JS that a
 * media source is now open.
 *
 * @param {String} msObjectURL string referencing the MSE Object URL
 * @param {String} swfId the swf id
 */
var open = function open(msObjectURL, swfId) {
  var mediaSource = _holaOrgVideoJs2['default'].mediaSources[msObjectURL];

  if (mediaSource) {
    mediaSource.trigger({ type: 'sourceopen', swfId: swfId });
  } else {
    throw new Error('Media Source not found (Video.js)');
  }
};

/**
 * Check to see if the native MediaSource object exists and supports
 * an MP4 container with both H.264 video and AAC-LC audio.
 *
 * @return {Boolean} if  native media sources are supported
 */
var supportsNativeMediaSources = function supportsNativeMediaSources() {
  return !!window.MediaSource && !!window.MediaSource.isTypeSupported && window.MediaSource.isTypeSupported('video/mp4;codecs="avc1.4d400d,mp4a.40.2"');
};

/**
 * An emulation of the MediaSource API so that we can support
 * native and non-native functionality such as flash and
 * video/mp2t videos. returns an instance of HtmlMediaSource or
 * FlashMediaSource depending on what is supported and what options
 * are passed in.
 *
 * @link https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/MediaSource
 * @param {Object} options options to use during setup.
 */
var MediaSource = function MediaSource(options) {
  var settings = _holaOrgVideoJs2['default'].mergeOptions(defaults, options);

  this.MediaSource = {
    open: open,
    supportsNativeMediaSources: supportsNativeMediaSources
  };

  // determine whether HTML MediaSources should be used
  if (settings.mode === 'html5' || settings.mode === 'auto' && supportsNativeMediaSources()) {
    return new _htmlMediaSource2['default']();
  }

  // otherwise, emulate them through the SWF
  return new _flashMediaSource2['default']();
};

exports.MediaSource = MediaSource;
MediaSource.open = open;
MediaSource.supportsNativeMediaSources = supportsNativeMediaSources;

/**
 * A wrapper around the native URL for our MSE object
 * implementation, this object is exposed under videojs.URL
 *
 * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
 */
var URL = {
  /**
   * A wrapper around the native createObjectURL for our objects.
   * This function maps a native or emulated mediaSource to a blob
   * url so that it can be loaded into video.js
   *
   * @link https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
   * @param {MediaSource} object the object to create a blob url to
   */
  createObjectURL: function createObjectURL(object) {
    var objectUrlPrefix = 'blob:vjs-media-source/';
    var url = undefined;

    // use the native MediaSource to generate an object URL
    if (object instanceof _htmlMediaSource2['default']) {
      url = window.URL.createObjectURL(object.nativeMediaSource_);
      object.url_ = url;
      return url;
    }
    // if the object isn't an emulated MediaSource, delegate to the
    // native implementation
    if (!(object instanceof _flashMediaSource2['default'])) {
      url = window.URL.createObjectURL(object);
      object.url_ = url;
      return url;
    }

    // build a URL that can be used to map back to the emulated
    // MediaSource
    url = objectUrlPrefix + urlCount;

    urlCount++;

    // setup the mapping back to object
    _holaOrgVideoJs2['default'].mediaSources[url] = object;

    return url;
  }
};

exports.URL = URL;
_holaOrgVideoJs2['default'].MediaSource = MediaSource;
_holaOrgVideoJs2['default'].URL = URL;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{"./flash-media-source":13,"./html-media-source":15}],19:[function(_dereq_,module,exports){
(function (global){
/**
 * @file virtual-source-buffer.js
 */
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _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; }; })();

var _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); } } };

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _holaOrgVideoJs = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);

var _holaOrgVideoJs2 = _interopRequireDefault(_holaOrgVideoJs);

var _createTextTracksIfNecessary = _dereq_('./create-text-tracks-if-necessary');

var _createTextTracksIfNecessary2 = _interopRequireDefault(_createTextTracksIfNecessary);

var _removeCuesFromTrack = _dereq_('./remove-cues-from-track');

var _removeCuesFromTrack2 = _interopRequireDefault(_removeCuesFromTrack);

var _addTextTrackData = _dereq_('./add-text-track-data');

var _addTextTrackData2 = _interopRequireDefault(_addTextTrackData);

var _webworkify = _dereq_('webworkify');

var _webworkify2 = _interopRequireDefault(_webworkify);

var _transmuxerWorker = _dereq_('./transmuxer-worker');

var _transmuxerWorker2 = _interopRequireDefault(_transmuxerWorker);

var _codecUtils = _dereq_('./codec-utils');

/**
 * VirtualSourceBuffers exist so that we can transmux non native formats
 * into a native format, but keep the same api as a native source buffer.
 * It creates a transmuxer, that works in its own thread (a web worker) and
 * that transmuxer muxes the data into a native format. VirtualSourceBuffer will
 * then send all of that data to the naive sourcebuffer so that it is
 * indestinguishable from a natively supported format.
 *
 * @param {HtmlMediaSource} mediaSource the parent mediaSource
 * @param {Array} codecs array of codecs that we will be dealing with
 * @class VirtualSourceBuffer
 * @extends video.js.EventTarget
 */

var VirtualSourceBuffer = (function (_videojs$EventTarget) {
  _inherits(VirtualSourceBuffer, _videojs$EventTarget);

  function VirtualSourceBuffer(mediaSource, codecs) {
    var _this = this;

    _classCallCheck(this, VirtualSourceBuffer);

    _get(Object.getPrototypeOf(VirtualSourceBuffer.prototype), 'constructor', this).call(this, _holaOrgVideoJs2['default'].EventTarget);
    this.timestampOffset_ = 0;
    this.pendingBuffers_ = [];
    this.bufferUpdating_ = false;
    this.mediaSource_ = mediaSource;
    this.codecs_ = codecs;
    this.audioCodec_ = null;
    this.videoCodec_ = null;
    this.audioDisabled_ = false;

    var options = {
      remux: false
    };

    this.codecs_.forEach(function (codec) {
      if ((0, _codecUtils.isAudioCodec)(codec)) {
        _this.audioCodec_ = codec;
      } else if ((0, _codecUtils.isVideoCodec)(codec)) {
        _this.videoCodec_ = codec;
      }
    });

    // append muxed segments to their respective native buffers as
    // soon as they are available
    this.transmuxer_ = (0, _webworkify2['default'])(_transmuxerWorker2['default']);
    this.transmuxer_.postMessage({ action: 'init', options: options });

    this.transmuxer_.onmessage = function (event) {
      if (event.data.action === 'data') {
        return _this.data_(event);
      }

      if (event.data.action === 'done') {
        return _this.done_(event);
      }
    };

    // this timestampOffset is a property with the side-effect of resetting
    // baseMediaDecodeTime in the transmuxer on the setter
    Object.defineProperty(this, 'timestampOffset', {
      get: function get() {
        return this.timestampOffset_;
      },
      set: function set(val) {
        if (typeof val === 'number' && val >= 0) {
          this.timestampOffset_ = val;

          // We have to tell the transmuxer to set the baseMediaDecodeTime to
          // the desired timestampOffset for the next segment
          this.transmuxer_.postMessage({
            action: 'setTimestampOffset',
            timestampOffset: val
          });
        }
      }
    });

    // setting the append window affects both source buffers
    Object.defineProperty(this, 'appendWindowStart', {
      get: function get() {
        return (this.videoBuffer_ || this.audioBuffer_).appendWindowStart;
      },
      set: function set(start) {
        if (this.videoBuffer_) {
          this.videoBuffer_.appendWindowStart = start;
        }
        if (this.audioBuffer_) {
          this.audioBuffer_.appendWindowStart = start;
        }
      }
    });

    // this buffer is "updating" if either of its native buffers are
    Object.defineProperty(this, 'updating', {
      get: function get() {
        return !!(this.bufferUpdating_ || !this.audioDisabled_ && this.audioBuffer_ && this.audioBuffer_.updating || this.videoBuffer_ && this.videoBuffer_.updating);
      }
    });

    // the buffered property is the intersection of the buffered
    // ranges of the native source buffers
    Object.defineProperty(this, 'buffered', {
      get: function get() {
        var start = null;
        var end = null;
        var arity = 0;
        var extents = [];
        var ranges = [];

        if (!this.videoBuffer_ && (this.audioDisabled_ || !this.audioBuffer_)) {
          return _holaOrgVideoJs2['default'].createTimeRange();
        }

        // Handle the case where we only have one buffer
        if (!this.videoBuffer_) {
          return this.audioBuffer_.buffered;
        } else if (this.audioDisabled_ || !this.audioBuffer_) {
          return this.videoBuffer_.buffered;
        }

        // Handle the case where there is no buffer data
        if ((!this.videoBuffer_ || this.videoBuffer_.buffered.length === 0) && (!this.audioBuffer_ || this.audioBuffer_.buffered.length === 0)) {
          return _holaOrgVideoJs2['default'].createTimeRange();
        }

        // Handle the case where we have both buffers and create an
        // intersection of the two
        var videoBuffered = this.videoBuffer_.buffered;
        var audioBuffered = this.audioBuffer_.buffered;
        var count = videoBuffered.length;

        // A) Gather up all start and end times
        while (count--) {
          extents.push({ time: videoBuffered.start(count), type: 'start' });
          extents.push({ time: videoBuffered.end(count), type: 'end' });
        }
        count = audioBuffered.length;
        while (count--) {
          extents.push({ time: audioBuffered.start(count), type: 'start' });
          extents.push({ time: audioBuffered.end(count), type: 'end' });
        }
        // B) Sort them by time
        extents.sort(function (a, b) {
          return a.time - b.time;
        });

        // C) Go along one by one incrementing arity for start and decrementing
        //    arity for ends
        for (count = 0; count < extents.length; count++) {
          if (extents[count].type === 'start') {
            arity++;

            // D) If arity is ever incremented to 2 we are entering an
            //    overlapping range
            if (arity === 2) {
              start = extents[count].time;
            }
          } else if (extents[count].type === 'end') {
            arity--;

            // E) If arity is ever decremented to 1 we leaving an
            //    overlapping range
            if (arity === 1) {
              end = extents[count].time;
            }
          }

          // F) Record overlapping ranges
          if (start !== null && end !== null) {
            ranges.push([start, end]);
            start = null;
            end = null;
          }
        }

        return _holaOrgVideoJs2['default'].createTimeRanges(ranges);
      }
    });
  }

  /**
   * When we get a data event from the transmuxer
   * we call this function and handle the data that
   * was sent to us
   *
   * @private
   * @param {Event} event the data event from the transmuxer
   */

  _createClass(VirtualSourceBuffer, [{
    key: 'data_',
    value: function data_(event) {
      var segment = event.data.segment;

      // Cast ArrayBuffer to TypedArray
      segment.data = new Uint8Array(segment.data, event.data.byteOffset, event.data.byteLength);

      (0, _createTextTracksIfNecessary2['default'])(this, this.mediaSource_, segment);

      // Add the segments to the pendingBuffers array
      this.pendingBuffers_.push(segment);
      return;
    }

    /**
     * When we get a done event from the transmuxer
     * we call this function and we process all
     * of the pending data that we have been saving in the
     * data_ function
     *
     * @private
     * @param {Event} event the done event from the transmuxer
     */
  }, {
    key: 'done_',
    value: function done_(event) {
      // All buffers should have been flushed from the muxer
      // start processing anything we have received
      this.processPendingSegments_();
      return;
    }

    /**
     * Create our internal native audio/video source buffers and add
     * event handlers to them with the following conditions:
     * 1. they do not already exist on the mediaSource
     * 2. this VSB has a codec for them
     *
     * @private
     */
  }, {
    key: 'createRealSourceBuffers_',
    value: function createRealSourceBuffers_() {
      var _this2 = this;

      var types = ['audio', 'video'];

      types.forEach(function (type) {
        // Don't create a SourceBuffer of this type if we don't have a
        // codec for it
        if (!_this2[type + 'Codec_']) {
          return;
        }

        // Do nothing if a SourceBuffer of this type already exists
        if (_this2[type + 'Buffer_']) {
          return;
        }

        var buffer = null;

        // If the mediasource already has a SourceBuffer for the codec
        // use that
        if (_this2.mediaSource_[type + 'Buffer_']) {
          buffer = _this2.mediaSource_[type + 'Buffer_'];
        } else {
          buffer = _this2.mediaSource_.nativeMediaSource_.addSourceBuffer(type + '/mp4;codecs="' + _this2[type + 'Codec_'] + '"');
          _this2.mediaSource_[type + 'Buffer_'] = buffer;
        }

        _this2[type + 'Buffer_'] = buffer;

        // Wire up the events to the SourceBuffer
        ['update', 'updatestart', 'updateend'].forEach(function (event) {
          buffer.addEventListener(event, function () {
            // if audio is disabled
            if (type === 'audio' && _this2.audioDisabled_) {
              return;
            }

            var shouldTrigger = types.every(function (t) {
              // skip checking audio's updating status if audio
              // is not enabled
              if (t === 'audio' && _this2.audioDisabled_) {
                return true;
              }
              // if the other type if updating we don't trigger
              if (type !== t && _this2[t + 'Buffer_'] && _this2[t + 'Buffer_'].updating) {
                return false;
              }
              return true;
            });

            if (shouldTrigger) {
              return _this2.trigger(event);
            }
          });
        });
      });
    }

    /**
     * Emulate the native mediasource function, but our function will
     * send all of the proposed segments to the transmuxer so that we
     * can transmux them before we append them to our internal
     * native source buffers in the correct format.
     *
     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/appendBuffer
     * @param {Uint8Array} segment the segment to append to the buffer
     */
  }, {
    key: 'appendBuffer',
    value: function appendBuffer(segment) {
      // Start the internal "updating" state
      this.bufferUpdating_ = true;

      this.transmuxer_.postMessage({
        action: 'push',
        // Send the typed-array of data as an ArrayBuffer so that
        // it can be sent as a "Transferable" and avoid the costly
        // memory copy
        data: segment.buffer,

        // To recreate the original typed-array, we need information
        // about what portion of the ArrayBuffer it was a view into
        byteOffset: segment.byteOffset,
        byteLength: segment.byteLength
      }, [segment.buffer]);
      this.transmuxer_.postMessage({ action: 'flush' });
    }
  }, {
    key: 'pushBuffer',
    value: function pushBuffer(segment) {
      this.transmuxer_.postMessage({
        action: 'push',
        // Send the typed-array of data as an ArrayBuffer so that
        // it can be sent as a "Transferable" and avoid the costly
        // memory copy
        data: segment.buffer,

        // To recreate the original typed-array, we need information
        // about what portion of the ArrayBuffer it was a view into
        byteOffset: segment.byteOffset,
        byteLength: segment.byteLength
      }, [segment.buffer]);
    }
  }, {
    key: 'flushBuffer',
    value: function flushBuffer() {
      this.bufferUpdating_ = true;
      this.transmuxer_.postMessage({ action: 'flush' });
    }

    /**
     * Emulate the native mediasource function and remove parts
     * of the buffer from any of our internal buffers that exist
     *
     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/remove
     * @param {Double} start position to start the remove at
     * @param {Double} end position to end the remove at
     */
  }, {
    key: 'remove',
    value: function remove(start, end) {
      if (this.videoBuffer_) {
        this.videoBuffer_.remove(start, end);
      }
      if (!this.audioDisabled_ && this.audioBuffer_) {
        this.audioBuffer_.remove(start, end);
      }

      // Remove Metadata Cues (id3)
      (0, _removeCuesFromTrack2['default'])(start, end, this.metadataTrack_);

      // Remove Any Captions
      (0, _removeCuesFromTrack2['default'])(start, end, this.inbandTextTrack_);
    }

    /**
     * Process any segments that the muxer has output
     * Concatenate segments together based on type and append them into
     * their respective sourceBuffers
     *
     * @private
     */
  }, {
    key: 'processPendingSegments_',
    value: function processPendingSegments_() {
      var sortedSegments = {
        video: {
          segments: [],
          bytes: 0
        },
        audio: {
          segments: [],
          bytes: 0
        },
        captions: [],
        metadata: []
      };

      // Sort segments into separate video/audio arrays and
      // keep track of their total byte lengths
      sortedSegments = this.pendingBuffers_.reduce(function (segmentObj, segment) {
        var type = segment.type;
        var data = segment.data;

        segmentObj[type].segments.push(data);
        segmentObj[type].bytes += data.byteLength;

        // Gather any captions into a single array
        if (segment.captions) {
          segmentObj.captions = segmentObj.captions.concat(segment.captions);
        }

        if (segment.info) {
          segmentObj[type].info = segment.info;
        }

        // Gather any metadata into a single array
        if (segment.metadata) {
          segmentObj.metadata = segmentObj.metadata.concat(segment.metadata);
        }

        return segmentObj;
      }, sortedSegments);

      // Create the real source buffers if they don't exist by now since we
      // finally are sure what tracks are contained in the source
      if (!this.videoBuffer_ && !this.audioBuffer_) {
        // Remove any codecs that may have been specified by default but
        // are no longer applicable now
        if (sortedSegments.video.bytes === 0) {
          this.videoCodec_ = null;
        }
        if (sortedSegments.audio.bytes === 0) {
          this.audioCodec_ = null;
        }

        this.createRealSourceBuffers_();
      }

      if (sortedSegments.audio.info) {
        this.mediaSource_.trigger({ type: 'audioinfo', info: sortedSegments.audio.info });
      }
      if (sortedSegments.video.info) {
        this.mediaSource_.trigger({ type: 'videoinfo', info: sortedSegments.video.info });
      }

      // Merge multiple video and audio segments into one and append
      if (this.videoBuffer_) {
        this.concatAndAppendSegments_(sortedSegments.video, this.videoBuffer_);
        // TODO: are video tracks the only ones with text tracks?
        (0, _addTextTrackData2['default'])(this, sortedSegments.captions, sortedSegments.metadata);
      }
      if (!this.audioDisabled_ && this.audioBuffer_) {
        this.concatAndAppendSegments_(sortedSegments.audio, this.audioBuffer_);
      }

      this.pendingBuffers_.length = 0;

      // We are no longer in the internal "updating" state
      this.bufferUpdating_ = false;
    }

    /**
     * Combine all segments into a single Uint8Array and then append them
     * to the destination buffer
     *
     * @param {Object} segmentObj
     * @param {SourceBuffer} destinationBuffer native source buffer to append data to
     * @private
     */
  }, {
    key: 'concatAndAppendSegments_',
    value: function concatAndAppendSegments_(segmentObj, destinationBuffer) {
      var offset = 0;
      var tempBuffer = undefined;

      if (segmentObj.bytes) {
        tempBuffer = new Uint8Array(segmentObj.bytes);

        // Combine the individual segments into one large typed-array
        segmentObj.segments.forEach(function (segment) {
          tempBuffer.set(segment, offset);
          offset += segment.byteLength;
        });

        destinationBuffer.appendBuffer(tempBuffer);
      }
    }

    /**
     * Emulate the native mediasource function. abort any soureBuffer
     * actions and throw out any un-appended data.
     *
     * @link https://developer.mozilla.org/en-US/docs/Web/API/SourceBuffer/abort
     */
  }, {
    key: 'abort',
    value: function abort() {
      if (this.videoBuffer_) {
        this.videoBuffer_.abort();
      }
      if (this.audioBuffer_) {
        this.audioBuffer_.abort();
      }
      if (this.transmuxer_) {
        this.transmuxer_.postMessage({ action: 'reset' });
      }
      this.pendingBuffers_.length = 0;
      this.bufferUpdating_ = false;
    }
  }]);

  return VirtualSourceBuffer;
})(_holaOrgVideoJs2['default'].EventTarget);

exports['default'] = VirtualSourceBuffer;
module.exports = exports['default'];
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{"./add-text-track-data":9,"./codec-utils":10,"./create-text-tracks-if-necessary":11,"./remove-cues-from-track":16,"./transmuxer-worker":17,"webworkify":42}],20:[function(_dereq_,module,exports){
/**
 * mux.js
 *
 * Copyright (c) 2016 Brightcove
 * All rights reserved.
 *
 * A stream-based aac to mp4 converter. This utility can be used to
 * deliver mp4s to a SourceBuffer on platforms that support native
 * Media Source Extensions.
 */
'use strict';
var Stream = _dereq_('../utils/stream.js');

// Constants
var AacStream;

/**
 * Splits an incoming stream of binary data into ADTS and ID3 Frames.
 */

AacStream = function() {
  var
    everything = new Uint8Array(),
    timeStamp = 0;

  AacStream.prototype.init.call(this);

  this.setTimestamp = function(timestamp) {
    timeStamp = timestamp;
  };

  this.parseId3TagSize = function(header, byteIndex) {
    var
      returnSize = (header[byteIndex + 6] << 21) |
                   (header[byteIndex + 7] << 14) |
                   (header[byteIndex + 8] << 7) |
                   (header[byteIndex + 9]),
      flags = header[byteIndex + 5],
      footerPresent = (flags & 16) >> 4;

    if (footerPresent) {
      return returnSize + 20;
    }
    return returnSize + 10;
  };

  this.parseAdtsSize = function(header, byteIndex) {
    var
      lowThree = (header[byteIndex + 5] & 0xE0) >> 5,
      middle = header[byteIndex + 4] << 3,
      highTwo = header[byteIndex + 3] & 0x3 << 11;

    return (highTwo | middle) | lowThree;
  };

  this.push = function(bytes) {
    var
      frameSize = 0,
      byteIndex = 0,
      bytesLeft,
      chunk,
      packet,
      tempLength;

    // If there are bytes remaining from the last segment, prepend them to the
    // bytes that were pushed in
    if (everything.length) {
      tempLength = everything.length;
      everything = new Uint8Array(bytes.byteLength + tempLength);
      everything.set(everything.subarray(0, tempLength));
      everything.set(bytes, tempLength);
    } else {
      everything = bytes;
    }

    while (everything.length - byteIndex >= 3) {
      if ((everything[byteIndex] === 'I'.charCodeAt(0)) &&
          (everything[byteIndex + 1] === 'D'.charCodeAt(0)) &&
          (everything[byteIndex + 2] === '3'.charCodeAt(0))) {

        // Exit early because we don't have enough to parse
        // the ID3 tag header
        if (everything.length - byteIndex < 10) {
          break;
        }

        // check framesize
        frameSize = this.parseId3TagSize(everything, byteIndex);

        // Exit early if we don't have enough in the buffer
        // to emit a full packet
        if (frameSize > everything.length) {
          break;
        }
        chunk = {
          type: 'timed-metadata',
          data: everything.subarray(byteIndex, byteIndex + frameSize)
        };
        this.trigger('data', chunk);
        byteIndex += frameSize;
        continue;
      } else if ((everything[byteIndex] & 0xff === 0xff) &&
                 ((everything[byteIndex + 1] & 0xf0) === 0xf0)) {

        // Exit early because we don't have enough to parse
        // the ADTS frame header
        if (everything.length - byteIndex < 7) {
          break;
        }

        frameSize = this.parseAdtsSize(everything, byteIndex);

        // Exit early if we don't have enough in the buffer
        // to emit a full packet
        if (frameSize > everything.length) {
          break;
        }

        packet = {
          type: 'audio',
          data: everything.subarray(byteIndex, byteIndex + frameSize),
          pts: timeStamp,
          dts: timeStamp
        };
        this.trigger('data', packet);
        byteIndex += frameSize;
        continue;
      }
      byteIndex++;
    }
    bytesLeft = everything.length - byteIndex;

    if (bytesLeft > 0) {
      everything = everything.subarray(byteIndex);
    } else {
      everything = new Uint8Array();
    }
  };
};

AacStream.prototype = new Stream();

module.exports = AacStream;

},{"../utils/stream.js":41}],21:[function(_dereq_,module,exports){
'use strict';

var Stream = _dereq_('../utils/stream.js');

var AdtsStream;

var
  ADTS_SAMPLING_FREQUENCIES = [
    96000,
    88200,
    64000,
    48000,
    44100,
    32000,
    24000,
    22050,
    16000,
    12000,
    11025,
    8000,
    7350
  ];

/*
 * Accepts a ElementaryStream and emits data events with parsed
 * AAC Audio Frames of the individual packets. Input audio in ADTS
 * format is unpacked and re-emitted as AAC frames.
 *
 * @see http://wiki.multimedia.cx/index.php?title=ADTS
 * @see http://wiki.multimedia.cx/?title=Understanding_AAC
 */
AdtsStream = function() {
  var buffer;

  AdtsStream.prototype.init.call(this);

  this.push = function(packet) {
    var
      i = 0,
      frameNum = 0,
      frameLength,
      protectionSkipBytes,
      frameEnd,
      oldBuffer,
      sampleCount,
      adtsFrameDuration;

    if (packet.type !== 'audio') {
      // ignore non-audio data
      return;
    }

    // Prepend any data in the buffer to the input data so that we can parse
    // aac frames the cross a PES packet boundary
    if (buffer) {
      oldBuffer = buffer;
      buffer = new Uint8Array(oldBuffer.byteLength + packet.data.byteLength);
      buffer.set(oldBuffer);
      buffer.set(packet.data, oldBuffer.byteLength);
    } else {
      buffer = packet.data;
    }

    // unpack any ADTS frames which have been fully received
    // for details on the ADTS header, see http://wiki.multimedia.cx/index.php?title=ADTS
    while (i + 5 < buffer.length) {

      // Loook for the start of an ADTS header..
      if (buffer[i] !== 0xFF || (buffer[i + 1] & 0xF6) !== 0xF0) {
        // If a valid header was not found,  jump one forward and attempt to
        // find a valid ADTS header starting at the next byte
        i++;
        continue;
      }

      // The protection skip bit tells us if we have 2 bytes of CRC data at the
      // end of the ADTS header
      protectionSkipBytes = (~buffer[i + 1] & 0x01) * 2;

      // Frame length is a 13 bit integer starting 16 bits from the
      // end of the sync sequence
      frameLength = ((buffer[i + 3] & 0x03) << 11) |
        (buffer[i + 4] << 3) |
        ((buffer[i + 5] & 0xe0) >> 5);

      sampleCount = ((buffer[i + 6] & 0x03) + 1) * 1024;
      adtsFrameDuration = (sampleCount * 90000) /
        ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2];

      frameEnd = i + frameLength;

      // If we don't have enough data to actually finish this ADTS frame, return
      // and wait for more data
      if (buffer.byteLength < frameEnd) {
        return;
      }

      // Otherwise, deliver the complete AAC frame
      this.trigger('data', {
        pts: packet.pts + (frameNum * adtsFrameDuration),
        dts: packet.dts + (frameNum * adtsFrameDuration),
        sampleCount: sampleCount,
        audioobjecttype: ((buffer[i + 2] >>> 6) & 0x03) + 1,
        channelcount: ((buffer[i + 2] & 1) << 2) |
          ((buffer[i + 3] & 0xc0) >>> 6),
        samplerate: ADTS_SAMPLING_FREQUENCIES[(buffer[i + 2] & 0x3c) >>> 2],
        samplingfrequencyindex: (buffer[i + 2] & 0x3c) >>> 2,
        // assume ISO/IEC 14496-12 AudioSampleEntry default of 16
        samplesize: 16,
        data: buffer.subarray(i + 7 + protectionSkipBytes, frameEnd)
      });

      // If the buffer is empty, clear it and return
      if (buffer.byteLength === frameEnd) {
        buffer = undefined;
        return;
      }

      frameNum++;

      // Remove the finished frame from the buffer and start the process again
      buffer = buffer.subarray(frameEnd);
    }
  };
  this.flush = function() {
    this.trigger('done');
  };
};

AdtsStream.prototype = new Stream();

module.exports = AdtsStream;

},{"../utils/stream.js":41}],22:[function(_dereq_,module,exports){
'use strict';

var Stream = _dereq_('../utils/stream.js');
var ExpGolomb = _dereq_('../utils/exp-golomb.js');

var H264Stream, NalByteStream;
// values of profile_idc that indicate additional fields are included in the SPS
// see Recommendation ITU-T H.264 (4/2013),
// 7.3.2.1.1 Sequence parameter set data syntax
var PROFILES_WITH_OPTIONAL_SPS_DATA = {
  100: true,
  110: true,
  122: true,
  244: true,
  44: true,
  83: true,
  86: true,
  118: true,
  128: true,
  138: true,
  139: true,
  134: true
};
var unitTypes = {
    slice_layer_without_partitioning_rbsp: 1,
    slice_layer_without_partitioning_rbsp_idr: 5,
    sei_rbsp: 6,
    seq_parameter_set_rbsp: 7,
    pic_parameter_set_rbsp: 8,
    access_unit_delimiter_rbsp: 9,
};

/**
 * Accepts a NAL unit byte stream and unpacks the embedded NAL units.
 */
NalByteStream = function() {
  var
    syncPoint = 0,
    i,
    buffer;
  NalByteStream.prototype.init.call(this);

  this.push = function(data) {
    var swapBuffer;

    if (!buffer) {
      buffer = data.data;
    } else {
      swapBuffer = new Uint8Array(buffer.byteLength + data.data.byteLength);
      swapBuffer.set(buffer);
      swapBuffer.set(data.data, buffer.byteLength);
      buffer = swapBuffer;
    }

    // Rec. ITU-T H.264, Annex B
    // scan for NAL unit boundaries

    // a match looks like this:
    // 0 0 1 .. NAL .. 0 0 1
    // ^ sync point        ^ i
    // or this:
    // 0 0 1 .. NAL .. 0 0 0
    // ^ sync point        ^ i

    // advance the sync point to a NAL start, if necessary
    for (; syncPoint < buffer.byteLength - 3; syncPoint++) {
      if (buffer[syncPoint + 2] === 1) {
        // the sync point is properly aligned
        i = syncPoint + 5;
        break;
      }
    }

    while (i < buffer.byteLength) {
      // look at the current byte to determine if we've hit the end of
      // a NAL unit boundary
      switch (buffer[i]) {
      case 0:
        // skip past non-sync sequences
        if (buffer[i - 1] !== 0) {
          i += 2;
          break;
        } else if (buffer[i - 2] !== 0) {
          i++;
          break;
        }

        // deliver the NAL unit if it isn't empty
        if (syncPoint + 3 !== i - 2) {
          this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
        }

        // drop trailing zeroes
        do {
          i++;
        } while (buffer[i] !== 1 && i < buffer.length);
        syncPoint = i - 2;
        i += 3;
        break;
      case 1:
        // skip past non-sync sequences
        if (buffer[i - 1] !== 0 ||
            buffer[i - 2] !== 0) {
          i += 3;
          break;
        }

        // deliver the NAL unit
        this.trigger('data', buffer.subarray(syncPoint + 3, i - 2));
        syncPoint = i - 2;
        i += 3;
        break;
      default:
        // the current byte isn't a one or zero, so it cannot be part
        // of a sync sequence
        i += 3;
        break;
      }
    }
    // filter out the NAL units that were delivered
    buffer = buffer.subarray(syncPoint);
    i -= syncPoint;
    syncPoint = 0;
  };

  this.flush = function() {
    // deliver the last buffered NAL unit
    if (buffer && buffer.byteLength > 3) {
      this.trigger('data', buffer.subarray(syncPoint + 3));
    }
    // reset the stream state
    buffer = null;
    syncPoint = 0;
    this.trigger('done');
  };
};
NalByteStream.prototype = new Stream();

/**
 * Accepts input from a ElementaryStream and produces H.264 NAL unit data
 * events.
 */
H264Stream = function() {
  var
    nalByteStream = new NalByteStream(),
    self,
    trackId,
    currentPts,
    currentDts,

    discardEmulationPreventionBytes,
    readSequenceParameterSet,
    skipScalingList;

  H264Stream.prototype.init.call(this);
  self = this;

  this.push = function(packet) {
    if (packet.type !== 'video') {
      return;
    }
    trackId = packet.trackId;
    currentPts = packet.pts;
    currentDts = packet.dts;

    nalByteStream.push(packet);
  };

  nalByteStream.on('data', function(data) {
    var
      event = {
        trackId: trackId,
        pts: currentPts,
        dts: currentDts,
        data: data
      };
    var ev_type = data[0] & 0x1f;

    if (ev_type==1 || ev_type>=5 && ev_type<=9) {
        event.nalUnitType = ev_type;
    }
    if (ev_type==7 || ev_type==6) {
      event.escapedRBSP = discardEmulationPreventionBytes(data.subarray(1));
      event.config = ev_type==7 ? readSequenceParameterSet(event.escapedRBSP) : null;
    }
    self.trigger('data', event);
  });
  nalByteStream.on('done', function() { self.trigger('done'); });
  this.flush = function() { nalByteStream.flush(); };

  /**
   * Advance the ExpGolomb decoder past a scaling list. The scaling
   * list is optionally transmitted as part of a sequence parameter
   * set and is not relevant to transmuxing.
   * @param count {number} the number of entries in this scaling list
   * @param expGolombDecoder {object} an ExpGolomb pointed to the
   * start of a scaling list
   * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
   */
  skipScalingList = function(count, expGolombDecoder) {
    var
      lastScale = 8,
      nextScale = 8,
      j,
      deltaScale;

    for (j = 0; j < count; j++) {
      if (nextScale !== 0) {
        deltaScale = expGolombDecoder.readExpGolomb();
        nextScale = (lastScale + deltaScale + 256) % 256;
      }

      lastScale = (nextScale === 0) ? lastScale : nextScale;
    }
  };

  /**
   * Expunge any "Emulation Prevention" bytes from a "Raw Byte
   * Sequence Payload"
   * @param data {Uint8Array} the bytes of a RBSP from a NAL
   * unit
   * @return {Uint8Array} the RBSP without any Emulation
   * Prevention Bytes
   */
  discardEmulationPreventionBytes = function(data) {
    var
      length = data.byteLength,
      emulationPreventionBytesPositions = [],
      i = 1,
      newLength, newData;

    // Find all `Emulation Prevention Bytes`
    while (i < length - 2) {
      if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
        emulationPreventionBytesPositions.push(i + 2);
        i += 2;
      } else {
        i++;
      }
    }

    // If no Emulation Prevention Bytes were found just return the original
    // array
    if (emulationPreventionBytesPositions.length === 0) {
      return data;
    }

    // Create a new array to hold the NAL unit data
    newLength = length - emulationPreventionBytesPositions.length;
    newData = new Uint8Array(newLength);
    var sourceIndex = 0;

    for (i = 0; i < newLength; sourceIndex++, i++) {
      if (sourceIndex === emulationPreventionBytesPositions[0]) {
        // Skip this byte
        sourceIndex++;
        // Remove this position index
        emulationPreventionBytesPositions.shift();
      }
      newData[i] = data[sourceIndex];
    }

    return newData;
  };

  /**
   * Read a sequence parameter set and return some interesting video
   * properties. A sequence parameter set is the H264 metadata that
   * describes the properties of upcoming video frames.
   * @param data {Uint8Array} the bytes of a sequence parameter set
   * @return {object} an object with configuration parsed from the
   * sequence parameter set, including the dimensions of the
   * associated video frames.
   */
  readSequenceParameterSet = function(data) {
    var
      frameCropLeftOffset = 0,
      frameCropRightOffset = 0,
      frameCropTopOffset = 0,
      frameCropBottomOffset = 0,
      sarScale = 1,
      expGolombDecoder, profileIdc, levelIdc, profileCompatibility,
      chromaFormatIdc, picOrderCntType,
      numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1,
      picHeightInMapUnitsMinus1,
      frameMbsOnlyFlag,
      scalingListCount,
      sarRatio,
      aspectRatioIdc,
      i;

    expGolombDecoder = new ExpGolomb(data);
    profileIdc = expGolombDecoder.readUnsignedByte(); // profile_idc
    profileCompatibility = expGolombDecoder.readUnsignedByte(); // constraint_set[0-5]_flag
    levelIdc = expGolombDecoder.readUnsignedByte(); // level_idc u(8)
    expGolombDecoder.skipUnsignedExpGolomb(); // seq_parameter_set_id

    // some profiles have more optional data we don't need
    if (PROFILES_WITH_OPTIONAL_SPS_DATA[profileIdc]) {
      chromaFormatIdc = expGolombDecoder.readUnsignedExpGolomb();
      if (chromaFormatIdc === 3) {
        expGolombDecoder.skipBits(1); // separate_colour_plane_flag
      }
      expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_luma_minus8
      expGolombDecoder.skipUnsignedExpGolomb(); // bit_depth_chroma_minus8
      expGolombDecoder.skipBits(1); // qpprime_y_zero_transform_bypass_flag
      if (expGolombDecoder.readBoolean()) { // seq_scaling_matrix_present_flag
        scalingListCount = (chromaFormatIdc !== 3) ? 8 : 12;
        for (i = 0; i < scalingListCount; i++) {
          if (expGolombDecoder.readBoolean()) { // seq_scaling_list_present_flag[ i ]
            if (i < 6) {
              skipScalingList(16, expGolombDecoder);
            } else {
              skipScalingList(64, expGolombDecoder);
            }
          }
        }
      }
    }

    expGolombDecoder.skipUnsignedExpGolomb(); // log2_max_frame_num_minus4
    picOrderCntType = expGolombDecoder.readUnsignedExpGolomb();

    if (picOrderCntType === 0) {
      expGolombDecoder.readUnsignedExpGolomb(); // log2_max_pic_order_cnt_lsb_minus4
    } else if (picOrderCntType === 1) {
      expGolombDecoder.skipBits(1); // delta_pic_order_always_zero_flag
      expGolombDecoder.skipExpGolomb(); // offset_for_non_ref_pic
      expGolombDecoder.skipExpGolomb(); // offset_for_top_to_bottom_field
      numRefFramesInPicOrderCntCycle = expGolombDecoder.readUnsignedExpGolomb();
      for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
        expGolombDecoder.skipExpGolomb(); // offset_for_ref_frame[ i ]
      }
    }

    expGolombDecoder.skipUnsignedExpGolomb(); // max_num_ref_frames
    expGolombDecoder.skipBits(1); // gaps_in_frame_num_value_allowed_flag

    picWidthInMbsMinus1 = expGolombDecoder.readUnsignedExpGolomb();
    picHeightInMapUnitsMinus1 = expGolombDecoder.readUnsignedExpGolomb();

    frameMbsOnlyFlag = expGolombDecoder.readBits(1);
    if (frameMbsOnlyFlag === 0) {
      expGolombDecoder.skipBits(1); // mb_adaptive_frame_field_flag
    }

    expGolombDecoder.skipBits(1); // direct_8x8_inference_flag
    if (expGolombDecoder.readBoolean()) { // frame_cropping_flag
      frameCropLeftOffset = expGolombDecoder.readUnsignedExpGolomb();
      frameCropRightOffset = expGolombDecoder.readUnsignedExpGolomb();
      frameCropTopOffset = expGolombDecoder.readUnsignedExpGolomb();
      frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
    }
    if (expGolombDecoder.readBoolean()) {
      // vui_parameters_present_flag
      if (expGolombDecoder.readBoolean()) {
        // aspect_ratio_info_present_flag
        aspectRatioIdc = expGolombDecoder.readUnsignedByte();
        switch (aspectRatioIdc) {
          case 1: sarRatio = [1, 1]; break;
          case 2: sarRatio = [12, 11]; break;
          case 3: sarRatio = [10, 11]; break;
          case 4: sarRatio = [16, 11]; break;
          case 5: sarRatio = [40, 33]; break;
          case 6: sarRatio = [24, 11]; break;
          case 7: sarRatio = [20, 11]; break;
          case 8: sarRatio = [32, 11]; break;
          case 9: sarRatio = [80, 33]; break;
          case 10: sarRatio = [18, 11]; break;
          case 11: sarRatio = [15, 11]; break;
          case 12: sarRatio = [64, 33]; break;
          case 13: sarRatio = [160, 99]; break;
          case 14: sarRatio = [4, 3]; break;
          case 15: sarRatio = [3, 2]; break;
          case 16: sarRatio = [2, 1]; break;
          case 255: {
            sarRatio = [expGolombDecoder.readUnsignedByte() << 8 |
                        expGolombDecoder.readUnsignedByte(),
                        expGolombDecoder.readUnsignedByte() << 8 |
                        expGolombDecoder.readUnsignedByte() ];
            break;
          }
        }
        if (sarRatio) {
          sarScale = sarRatio[0] / sarRatio[1];
        }
      }
    }
    return {
      profileIdc: profileIdc,
      levelIdc: levelIdc,
      profileCompatibility: profileCompatibility,
      width: Math.ceil((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2) * sarScale),
      height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) - (frameCropBottomOffset * 2)
    };
  };

};
H264Stream.prototype = new Stream();

module.exports = {
  H264Stream: H264Stream,
  NalByteStream: NalByteStream,
  unitTypes: unitTypes,
};

},{"../utils/exp-golomb.js":40,"../utils/stream.js":41}],23:[function(_dereq_,module,exports){
module.exports = {
  adts: _dereq_('./adts'),
  h264: _dereq_('./h264'),
};

},{"./adts":21,"./h264":22}],24:[function(_dereq_,module,exports){
/**
 * An object that stores the bytes of an FLV tag and methods for
 * querying and manipulating that data.
 * @see http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf
 */
'use strict';

var FlvTag;

// (type:uint, extraData:Boolean = false) extends ByteArray
FlvTag = function(type, extraData) {
  var
    // Counter if this is a metadata tag, nal start marker if this is a video
    // tag. unused if this is an audio tag
    adHoc = 0, // :uint

    // The default size is 16kb but this is not enough to hold iframe
    // data and the resizing algorithm costs a bit so we create a larger
    // starting buffer for video tags
    bufferStartSize = 16384,

    // checks whether the FLV tag has enough capacity to accept the proposed
    // write and re-allocates the internal buffers if necessary
    prepareWrite = function(flv, count) {
      var
        bytes,
        minLength = flv.position + count;
      if (minLength < flv.bytes.byteLength) {
        // there's enough capacity so do nothing
        return;
      }

      // allocate a new buffer and copy over the data that will not be modified
      bytes = new Uint8Array(minLength * 2);
      bytes.set(flv.bytes.subarray(0, flv.position), 0);
      flv.bytes = bytes;
      flv.view = new DataView(flv.bytes.buffer);
    },

    // commonly used metadata properties
    widthBytes = FlvTag.widthBytes || new Uint8Array('width'.length),
    heightBytes = FlvTag.heightBytes || new Uint8Array('height'.length),
    videocodecidBytes = FlvTag.videocodecidBytes || new Uint8Array('videocodecid'.length),
    i;

  if (!FlvTag.widthBytes) {
    // calculating the bytes of common metadata names ahead of time makes the
    // corresponding writes faster because we don't have to loop over the
    // characters
    // re-test with test/perf.html if you're planning on changing this
    for (i = 0; i < 'width'.length; i++) {
      widthBytes[i] = 'width'.charCodeAt(i);
    }
    for (i = 0; i < 'height'.length; i++) {
      heightBytes[i] = 'height'.charCodeAt(i);
    }
    for (i = 0; i < 'videocodecid'.length; i++) {
      videocodecidBytes[i] = 'videocodecid'.charCodeAt(i);
    }

    FlvTag.widthBytes = widthBytes;
    FlvTag.heightBytes = heightBytes;
    FlvTag.videocodecidBytes = videocodecidBytes;
  }

  this.keyFrame = false; // :Boolean

  switch(type) {
  case FlvTag.VIDEO_TAG:
    this.length = 16;
    // Start the buffer at 256k
    bufferStartSize *= 6;
    break;
  case FlvTag.AUDIO_TAG:
    this.length = 13;
    this.keyFrame = true;
    break;
  case FlvTag.METADATA_TAG:
    this.length = 29;
    this.keyFrame = true;
    break;
  default:
    throw("Error Unknown TagType");
  }

  this.bytes = new Uint8Array(bufferStartSize);
  this.view = new DataView(this.bytes.buffer);
  this.bytes[0] = type;
  this.position = this.length;
  this.keyFrame = extraData; // Defaults to false

  // presentation timestamp
  this.pts = 0;
  // decoder timestamp
  this.dts = 0;

  // ByteArray#writeBytes(bytes:ByteArray, offset:uint = 0, length:uint = 0)
  this.writeBytes = function(bytes, offset, length) {
    var
      start = offset || 0,
      end;
    length = length || bytes.byteLength;
    end = start + length;

    prepareWrite(this, length);
    this.bytes.set(bytes.subarray(start, end), this.position);

    this.position += length;
    this.length = Math.max(this.length, this.position);
  };

  // ByteArray#writeByte(value:int):void
  this.writeByte = function(byte) {
    prepareWrite(this, 1);
    this.bytes[this.position] = byte;
    this.position++;
    this.length = Math.max(this.length, this.position);
  };

  // ByteArray#writeShort(value:int):void
  this.writeShort = function(short) {
    prepareWrite(this, 2);
    this.view.setUint16(this.position, short);
    this.position += 2;
    this.length = Math.max(this.length, this.position);
  };

  // Negative index into array
  // (pos:uint):int
  this.negIndex = function(pos) {
    return this.bytes[this.length - pos];
  };

  // The functions below ONLY work when this[0] == VIDEO_TAG.
  // We are not going to check for that because we dont want the overhead
  // (nal:ByteArray = null):int
  this.nalUnitSize = function() {
    if (adHoc === 0) {
      return 0;
    }

    return this.length - (adHoc + 4);
  };

  this.startNalUnit = function() {
    // remember position and add 4 bytes
    if (adHoc > 0) {
      throw new Error("Attempted to create new NAL wihout closing the old one");
    }

    // reserve 4 bytes for nal unit size
    adHoc = this.length;
    this.length += 4;
    this.position = this.length;
  };

  // (nal:ByteArray = null):void
  this.endNalUnit = function(nalContainer) {
    var
      nalStart, // :uint
      nalLength; // :uint

    // Rewind to the marker and write the size
    if (this.length === adHoc + 4) {
      // we started a nal unit, but didnt write one, so roll back the 4 byte size value
      this.length -= 4;
    } else if (adHoc > 0) {
      nalStart = adHoc + 4;
      nalLength = this.length - nalStart;

      this.position = adHoc;
      this.view.setUint32(this.position, nalLength);
      this.position = this.length;

      if (nalContainer) {
        // Add the tag to the NAL unit
        nalContainer.push(this.bytes.subarray(nalStart, nalStart + nalLength));
      }
    }

    adHoc = 0;
  };

  /**
   * Write out a 64-bit floating point valued metadata property. This method is
   * called frequently during a typical parse and needs to be fast.
   */
  // (key:String, val:Number):void
  this.writeMetaDataDouble = function(key, val) {
    var i;
    prepareWrite(this, 2 + key.length + 9);

    // write size of property name
    this.view.setUint16(this.position, key.length);
    this.position += 2;

    // this next part looks terrible but it improves parser throughput by
    // 10kB/s in my testing

    // write property name
    if (key === 'width') {
      this.bytes.set(widthBytes, this.position);
      this.position += 5;
    } else if (key === 'height') {
      this.bytes.set(heightBytes, this.position);
      this.position += 6;
    } else if (key === 'videocodecid') {
      this.bytes.set(videocodecidBytes, this.position);
      this.position += 12;
    } else {
      for (i = 0; i < key.length; i++) {
        this.bytes[this.position] = key.charCodeAt(i);
        this.position++;
      }
    }

    // skip null byte
    this.position++;

    // write property value
    this.view.setFloat64(this.position, val);
    this.position += 8;

    // update flv tag length
    this.length = Math.max(this.length, this.position);
    ++adHoc;
  };

  // (key:String, val:Boolean):void
  this.writeMetaDataBoolean = function(key, val) {
    var i;
    prepareWrite(this, 2);
    this.view.setUint16(this.position, key.length);
    this.position += 2;
    for (i = 0; i < key.length; i++) {
      // if key.charCodeAt(i) >= 255, handle error
      prepareWrite(this, 1);
      this.bytes[this.position] = key.charCodeAt(i);
      this.position++;
    }
    prepareWrite(this, 2);
    this.view.setUint8(this.position, 0x01);
    this.position++;
    this.view.setUint8(this.position, val ? 0x01 : 0x00);
    this.position++;
    this.length = Math.max(this.length, this.position);
    ++adHoc;
  };

  // ():ByteArray
  this.finalize = function() {
    var
      dtsDelta, // :int
      len; // :int

    switch(this.bytes[0]) {
      // Video Data
    case FlvTag.VIDEO_TAG:
      this.bytes[11] = ((this.keyFrame || extraData) ? 0x10 : 0x20 ) | 0x07; // We only support AVC, 1 = key frame (for AVC, a seekable frame), 2 = inter frame (for AVC, a non-seekable frame)
      this.bytes[12] = extraData ?  0x00 : 0x01;

      dtsDelta = this.pts - this.dts;
      this.bytes[13] = (dtsDelta & 0x00FF0000) >>> 16;
      this.bytes[14] = (dtsDelta & 0x0000FF00) >>>  8;
      this.bytes[15] = (dtsDelta & 0x000000FF) >>>  0;
      break;

    case FlvTag.AUDIO_TAG:
      this.bytes[11] = 0xAF; // 44 kHz, 16-bit stereo
      this.bytes[12] = extraData ? 0x00 : 0x01;
      break;

    case FlvTag.METADATA_TAG:
      this.position = 11;
      this.view.setUint8(this.position, 0x02); // String type
      this.position++;
      this.view.setUint16(this.position, 0x0A); // 10 Bytes
      this.position += 2;
      // set "onMetaData"
      this.bytes.set([0x6f, 0x6e, 0x4d, 0x65,
                      0x74, 0x61, 0x44, 0x61,
                      0x74, 0x61], this.position);
      this.position += 10;
      this.bytes[this.position] = 0x08; // Array type
      this.position++;
      this.view.setUint32(this.position, adHoc);
      this.position = this.length;
      this.bytes.set([0, 0, 9], this.position);
      this.position += 3; // End Data Tag
      this.length = this.position;
      break;
    }

    len = this.length - 11;

    // write the DataSize field
    this.bytes[ 1] = (len & 0x00FF0000) >>> 16;
    this.bytes[ 2] = (len & 0x0000FF00) >>>  8;
    this.bytes[ 3] = (len & 0x000000FF) >>>  0;
    // write the Timestamp
    this.bytes[ 4] = (this.dts & 0x00FF0000) >>> 16;
    this.bytes[ 5] = (this.dts & 0x0000FF00) >>>  8;
    this.bytes[ 6] = (this.dts & 0x000000FF) >>>  0;
    this.bytes[ 7] = (this.dts & 0xFF000000) >>> 24;
    // write the StreamID
    this.bytes[ 8] = 0;
    this.bytes[ 9] = 0;
    this.bytes[10] = 0;

    // Sometimes we're at the end of the view and have one slot to write a
    // uint32, so, prepareWrite of count 4, since, view is uint8
    prepareWrite(this, 4);
    this.view.setUint32(this.length, this.length);
    this.length += 4;
    this.position += 4;

    // trim down the byte buffer to what is actually being used
    this.bytes = this.bytes.subarray(0, this.length);
    this.frameTime = FlvTag.frameTime(this.bytes);
    // if bytes.bytelength isn't equal to this.length, handle error
    return this;
  };
};

FlvTag.AUDIO_TAG = 0x08; // == 8, :uint
FlvTag.VIDEO_TAG = 0x09; // == 9, :uint
FlvTag.METADATA_TAG = 0x12; // == 18, :uint

// (tag:ByteArray):Boolean {
FlvTag.isAudioFrame = function(tag) {
  return FlvTag.AUDIO_TAG === tag[0];
};

// (tag:ByteArray):Boolean {
FlvTag.isVideoFrame = function(tag) {
  return FlvTag.VIDEO_TAG === tag[0];
};

// (tag:ByteArray):Boolean {
FlvTag.isMetaData = function(tag) {
  return FlvTag.METADATA_TAG === tag[0];
};

// (tag:ByteArray):Boolean {
FlvTag.isKeyFrame = function(tag) {
  if (FlvTag.isVideoFrame(tag)) {
    return tag[11] === 0x17;
  }

  if (FlvTag.isAudioFrame(tag)) {
    return true;
  }

  if (FlvTag.isMetaData(tag)) {
    return true;
  }

  return false;
};

// (tag:ByteArray):uint {
FlvTag.frameTime = function(tag) {
  var pts = tag[ 4] << 16; // :uint
  pts |= tag[ 5] <<  8;
  pts |= tag[ 6] <<  0;
  pts |= tag[ 7] << 24;
  return pts;
};

module.exports = FlvTag;

},{}],25:[function(_dereq_,module,exports){
module.exports = {
  tag: _dereq_('./flv-tag'),
  Transmuxer: _dereq_('./transmuxer'),
  tools: _dereq_('../tools/flv-inspector'),
};

},{"../tools/flv-inspector":38,"./flv-tag":24,"./transmuxer":26}],26:[function(_dereq_,module,exports){
'use strict';

var Stream = _dereq_('../utils/stream.js');
var FlvTag = _dereq_('./flv-tag.js');
var m2ts = _dereq_('../m2ts/m2ts.js');
var codecs = _dereq_('../codecs');
var AdtsStream = codecs.adts;
var H264Stream = codecs.h264.H264Stream;

var
  MetadataStream,
  Transmuxer,
  VideoSegmentStream,
  AudioSegmentStream,
  CoalesceStream,
  collectTimelineInfo,
  metaDataTag,
  extraDataTag;

/**
 * Store information about the start and end of the tracka and the
 * duration for each frame/sample we process in order to calculate
 * the baseMediaDecodeTime
 */
collectTimelineInfo = function (track, data) {
  if (typeof data.pts === 'number') {
    if (track.timelineStartInfo.pts === undefined) {
      track.timelineStartInfo.pts = data.pts;
    } else {
      track.timelineStartInfo.pts =
        Math.min(track.timelineStartInfo.pts, data.pts);
    }
  }

  if (typeof data.dts === 'number') {
    if (track.timelineStartInfo.dts === undefined) {
      track.timelineStartInfo.dts = data.dts;
    } else {
      track.timelineStartInfo.dts =
        Math.min(track.timelineStartInfo.dts, data.dts);
    }
  }
};

metaDataTag = function(track, pts) {
  var
    tag = new FlvTag(FlvTag.METADATA_TAG); // :FlvTag

  tag.dts = pts;
  tag.pts = pts;

  tag.writeMetaDataDouble("videocodecid", 7);
  tag.writeMetaDataDouble("width", track.width);
  tag.writeMetaDataDouble("height", track.height);

  return tag;
};

extraDataTag = function(track, pts) {
  var
    i,
    tag = new FlvTag(FlvTag.VIDEO_TAG, true);

  tag.dts = pts;
  tag.pts = pts;

  tag.writeByte(0x01);// version
  tag.writeByte(track.profileIdc);// profile
  tag.writeByte(track.profileCompatibility);// compatibility
  tag.writeByte(track.levelIdc);// level
  tag.writeByte(0xFC | 0x03); // reserved (6 bits), NULA length size - 1 (2 bits)
  tag.writeByte(0xE0 | 0x01 ); // reserved (3 bits), num of SPS (5 bits)
  tag.writeShort( track.sps[0].length ); // data of SPS
  tag.writeBytes( track.sps[0] ); // SPS

  tag.writeByte(track.pps.length); // num of PPS (will there ever be more that 1 PPS?)
  for (i = 0 ; i < track.pps.length ; ++i) {
    tag.writeShort(track.pps[i].length); // 2 bytes for length of PPS
    tag.writeBytes(track.pps[i]); // data of PPS
  }

  return tag;
};

/**
 * Constructs a single-track, media segment from AAC data
 * events. The output of this stream can be fed to flash.
 */
AudioSegmentStream = function(track) {
  var
    adtsFrames = [],
    adtsFramesLength = 0,
    sequenceNumber = 0,
    earliestAllowedDts = 0,
    oldExtraData;

  AudioSegmentStream.prototype.init.call(this);

  this.push = function(data) {
    collectTimelineInfo(track, data);

    if (track && track.channelcount === undefined) {
      track.audioobjecttype = data.audioobjecttype;
      track.channelcount = data.channelcount;
      track.samplerate = data.samplerate;
      track.samplingfrequencyindex = data.samplingfrequencyindex;
      track.samplesize = data.samplesize;
      track.extraData = (track.audioobjecttype << 11) |
                        (track.samplingfrequencyindex << 7) |
                        (track.channelcount << 3);
    }

    data.pts = Math.round(data.pts / 90);
    data.dts = Math.round(data.dts / 90);

    // buffer audio data until end() is called
    adtsFrames.push(data);
  };

  this.flush = function() {
    var currentFrame, adtsFrame, deltaDts,lastMetaPts, tags = [];
    // return early if no audio data has been observed
    if (adtsFrames.length === 0) {
      this.trigger('done');
      return;
    }

    lastMetaPts = -Infinity;

    while (adtsFrames.length) {
      currentFrame = adtsFrames.shift();

      // write out metadata tags every 1 second so that the decoder
      // is re-initialized quickly after seeking into a different
      // audio configuration
      if (track.extraData !== oldExtraData || currentFrame.pts - lastMetaPts >= 1000) {
       adtsFrame = new FlvTag(FlvTag.METADATA_TAG);
        adtsFrame.pts = currentFrame.pts;
        adtsFrame.dts = currentFrame.dts;

        // AAC is always 10
        adtsFrame.writeMetaDataDouble("audiocodecid", 10);
        adtsFrame.writeMetaDataBoolean("stereo", 2 === track.channelcount);
        adtsFrame.writeMetaDataDouble ("audiosamplerate", track.samplerate);
        // Is AAC always 16 bit?
        adtsFrame.writeMetaDataDouble ("audiosamplesize", 16);

        tags.push(adtsFrame);

        oldExtraData = track.extraData;

        adtsFrame = new FlvTag(FlvTag.AUDIO_TAG, true);
        // For audio, DTS is always the same as PTS. We want to set the DTS
        // however so we can compare with video DTS to determine approximate
        // packet order
        adtsFrame.pts = currentFrame.pts;
        adtsFrame.dts = currentFrame.dts;

        adtsFrame.view.setUint16(adtsFrame.position, track.extraData);
        adtsFrame.position += 2;
        adtsFrame.length = Math.max(adtsFrame.length, adtsFrame.position);

        tags.push(adtsFrame);

        lastMetaPts = currentFrame.pts;
      }
      adtsFrame = new FlvTag(FlvTag.AUDIO_TAG);
      adtsFrame.pts = currentFrame.pts;
      adtsFrame.dts = currentFrame.dts;

      adtsFrame.writeBytes(currentFrame.data);

      tags.push(adtsFrame);
    }

    oldExtraData = null;
    this.trigger('data', {track: track, tags: tags});

    this.trigger('done');
  };
};
AudioSegmentStream.prototype = new Stream();

/**
 * Store FlvTags for the h264 stream
 * @param track {object} track metadata configuration
 */
VideoSegmentStream = function(track) {
  var
    sequenceNumber = 0,
    nalUnits = [],
    nalUnitsLength = 0,
    config,
    h264Frame;
  VideoSegmentStream.prototype.init.call(this);

  this.finishFrame = function(tags, frame) {
    if (!frame) {
      return;
    }
    // Check if keyframe and the length of tags.
    // This makes sure we write metadata on the first frame of a segment.
    if (config && track && track.newMetadata &&
        (frame.keyFrame || tags.length === 0)) {
      // Push extra data on every IDR frame in case we did a stream change + seek
      tags.push(metaDataTag(config, frame.pts));
      tags.push(extraDataTag(track, frame.pts));
      track.newMetadata = false;
    }

    frame.endNalUnit();
    tags.push(frame);
  };

  this.push = function(data) {
    collectTimelineInfo(track, data);

    data.pts = Math.round(data.pts / 90);
    data.dts = Math.round(data.dts / 90);

    // buffer video until flush() is called
    nalUnits.push(data);
  };

  this.flush = function() {
    var
      currentNal,
      tags = [];

    // Throw away nalUnits at the start of the byte stream until we find
    // the first AUD
    while (nalUnits.length && nalUnits[0].nalUnitType !== codecs.h264.unitTypes.access_unit_delimiter_rbsp) {
      nalUnits.shift();
    }

    // return early if no video data has been observed
    if (nalUnits.length === 0) {
      this.trigger('done');
      return;
    }

    while (nalUnits.length) {
      currentNal = nalUnits.shift();

      // record the track config
      switch (currentNal.nalUnitType){
      case codecs.h264.unitTypes.seq_parameter_set_rbsp:
        track.newMetadata = true;
        config = currentNal.config;
        track.width = config.width;
        track.height = config.height;
        track.sps = [currentNal.data];
        track.profileIdc = config.profileIdc;
        track.levelIdc = config.levelIdc;
        track.profileCompatibility = config.profileCompatibility;
        h264Frame.endNalUnit();
        break;
      case codecs.h264.unitTypes.pic_parameter_set_rbsp:
        track.newMetadata = true;
        track.pps = [currentNal.data];
        h264Frame.endNalUnit();
        break;
      case codecs.h264.unitTypes.access_unit_delimiter_rbsp:
        if (h264Frame) {
          this.finishFrame(tags, h264Frame);
        }
        h264Frame = new FlvTag(FlvTag.VIDEO_TAG);
        h264Frame.pts = currentNal.pts;
        h264Frame.dts = currentNal.dts;
        break;
      case codecs.h264.unitTypes.slice_layer_without_partitioning_rbsp_idr:
        // the current sample is a key frame
        h264Frame.keyFrame = true;
        h264Frame.endNalUnit();
        break;
      default:
        h264Frame.endNalUnit();
        break;
      }
      h264Frame.startNalUnit();
      h264Frame.writeBytes(currentNal.data);
    }
    if (h264Frame) {
      this.finishFrame(tags, h264Frame);
    }

    this.trigger('data', {track: track, tags: tags});

    // Continue with the flush process now
    this.trigger('done');
  };
};

VideoSegmentStream.prototype = new Stream();

/**
 * The final stage of the transmuxer that emits the flv tags
 * for audio, video, and metadata. Also tranlates in time and
 * outputs caption data and id3 cues.
 */
CoalesceStream = function(options) {
  // Number of Tracks per output segment
  // If greater than 1, we combine multiple
  // tracks into a single segment
  this.numberOfTracks = 0;
  this.metadataStream = options.metadataStream;

  this.videoTags = [];
  this.audioTags = [];
  this.videoTrack = null;
  this.audioTrack = null;
  this.pendingCaptions = [];
  this.pendingMetadata = [];
  this.pendingTracks = 0;

  CoalesceStream.prototype.init.call(this);

  // Take output from multiple
  this.push = function(output) {
    // buffer incoming captions until the associated video segment
    // finishes
    if (output.text) {
      return this.pendingCaptions.push(output);
    }
    // buffer incoming id3 tags until the final flush
    if (output.frames) {
      return this.pendingMetadata.push(output);
    }

    if (output.track.type === 'video') {
      this.videoTrack = output.track;
      this.videoTags = output.tags;
      this.pendingTracks++;
    }
    if (output.track.type === 'audio') {
      this.audioTrack = output.track;
      this.audioTags = output.tags;
      this.pendingTracks++;
    }
  };
};

CoalesceStream.prototype = new Stream();
CoalesceStream.prototype.flush = function() {
  var
    id3,
    caption,
    i,
    timelineStartPts,
    event = {
      tags: {},
      captions: [],
      metadata: []
    };

  if (this.pendingTracks < this.numberOfTracks) {
    return;
  }

  if (this.videoTrack) {
    timelineStartPts = this.videoTrack.timelineStartInfo.pts;
  } else if (this.audioTrack) {
    timelineStartPts = this.audioTrack.timelineStartInfo.pts;
  }

  event.tags.videoTags = this.videoTags;
  event.tags.audioTags = this.audioTags;

  // Translate caption PTS times into second offsets into the
  // video timeline for the segment
  for (i = 0; i < this.pendingCaptions.length; i++) {
    caption = this.pendingCaptions[i];
    caption.startTime = caption.startPts - timelineStartPts;
    caption.startTime /= 90e3;
    caption.endTime = caption.endPts - timelineStartPts;
    caption.endTime /= 90e3;
    event.captions.push(caption);
  }

  // Translate ID3 frame PTS times into second offsets into the
  // video timeline for the segment
  for (i = 0; i < this.pendingMetadata.length; i++) {
    id3 = this.pendingMetadata[i];
    id3.cueTime = id3.pts - timelineStartPts;
    id3.cueTime /= 90e3;
    event.metadata.push(id3);
  }
  // We add this to every single emitted segment even though we only need
  // it for the first
  event.metadata.dispatchType = this.metadataStream.dispatchType;

  // Reset stream state
  this.videoTrack = null;
  this.audioTrack = null;
  this.videoTags = [];
  this.audioTags = [];
  this.pendingCaptions.length = 0;
  this.pendingMetadata.length = 0;
  this.pendingTracks = 0;

  // Emit the final segment
  this.trigger('data', event);

  this.trigger('done');
};

/**
 * An object that incrementally transmuxes MPEG2 Trasport Stream
 * chunks into an FLV.
 */
Transmuxer = function(options) {
  var
    self = this,
    videoTrack,
    audioTrack,

    packetStream, parseStream, elementaryStream,
    adtsStream, h264Stream,
    videoSegmentStream, audioSegmentStream, captionStream,
    coalesceStream;

  Transmuxer.prototype.init.call(this);

  options = options || {};

  // expose the metadata stream
  this.metadataStream = new m2ts.MetadataStream();

  options.metadataStream = this.metadataStream;

  // set up the parsing pipeline
  packetStream = new m2ts.TransportPacketStream();
  parseStream = new m2ts.TransportParseStream();
  elementaryStream = new m2ts.ElementaryStream();
  adtsStream = new AdtsStream();
  h264Stream = new H264Stream();
  coalesceStream = new CoalesceStream(options);

  // disassemble MPEG2-TS packets into elementary streams
  packetStream
    .pipe(parseStream)
    .pipe(elementaryStream);

  // !!THIS ORDER IS IMPORTANT!!
  // demux the streams
  elementaryStream
    .pipe(h264Stream);
  elementaryStream
    .pipe(adtsStream);

  elementaryStream
    .pipe(this.metadataStream)
    .pipe(coalesceStream);
  // if CEA-708 parsing is available, hook up a caption stream
  captionStream = new m2ts.CaptionStream();
  h264Stream.pipe(captionStream)
    .pipe(coalesceStream);

  // hook up the segment streams once track metadata is delivered
  elementaryStream.on('data', function(data) {
    var i, videoTrack, audioTrack;

    if (data.type === 'metadata') {
      i = data.tracks.length;

      // scan the tracks listed in the metadata
      while (i--) {
        if (data.tracks[i].type === 'video') {
          videoTrack = data.tracks[i];
        } else if (data.tracks[i].type === 'audio') {
          audioTrack = data.tracks[i];
        }
      }

      // hook up the video segment stream to the first track with h264 data
      if (videoTrack && !videoSegmentStream) {
        coalesceStream.numberOfTracks++;
        videoSegmentStream = new VideoSegmentStream(videoTrack);

        // Set up the final part of the video pipeline
        h264Stream
          .pipe(videoSegmentStream)
          .pipe(coalesceStream);
      }

      if (audioTrack && !audioSegmentStream) {
        // hook up the audio segment stream to the first track with aac data
        coalesceStream.numberOfTracks++;
        audioSegmentStream = new AudioSegmentStream(audioTrack);

        // Set up the final part of the audio pipeline
        adtsStream
          .pipe(audioSegmentStream)
          .pipe(coalesceStream);
      }
    }
  });

  // feed incoming data to the front of the parsing pipeline
  this.push = function(data) {
    packetStream.push(data);
  };

  // flush any buffered data
  this.flush = function() {
    // Start at the top of the pipeline and flush all pending work
    packetStream.flush();
  };

  // Re-emit any data coming from the coalesce stream to the outside world
  coalesceStream.on('data', function (event) {
    self.trigger('data', event);
  });

  // Let the consumer know we have finished flushing the entire pipeline
  coalesceStream.on('done', function () {
    self.trigger('done');
  });

  // For information on the FLV format, see
  // http://download.macromedia.com/f4v/video_file_format_spec_v10_1.pdf.
  // Technically, this function returns the header and a metadata FLV tag
  // if duration is greater than zero
  // duration in seconds
  // @return {object} the bytes of the FLV header as a Uint8Array
  this.getFlvHeader = function(duration, audio, video) { // :ByteArray {
    var
      headBytes = new Uint8Array(3 + 1 + 1 + 4),
      head = new DataView(headBytes.buffer),
      metadata,
      result,
      metadataLength;

    // default arguments
    duration = duration || 0;
    audio = audio === undefined? true : audio;
    video = video === undefined? true : video;

    // signature
    head.setUint8(0, 0x46); // 'F'
    head.setUint8(1, 0x4c); // 'L'
    head.setUint8(2, 0x56); // 'V'

    // version
    head.setUint8(3, 0x01);

    // flags
    head.setUint8(4, (audio ? 0x04 : 0x00) | (video ? 0x01 : 0x00));

    // data offset, should be 9 for FLV v1
    head.setUint32(5, headBytes.byteLength);

    // init the first FLV tag
    if (duration <= 0) {
      // no duration available so just write the first field of the first
      // FLV tag
      result = new Uint8Array(headBytes.byteLength + 4);
      result.set(headBytes);
      result.set([0, 0, 0, 0], headBytes.byteLength);
      return result;
    }

    // write out the duration metadata tag
    metadata = new FlvTag(FlvTag.METADATA_TAG);
    metadata.pts = metadata.dts = 0;
    metadata.writeMetaDataDouble("duration", duration);
    metadataLength = metadata.finalize().length;
    result = new Uint8Array(headBytes.byteLength + metadataLength);
    result.set(headBytes);
    result.set(head.byteLength, metadataLength);

    return result;
  };
};
Transmuxer.prototype = new Stream();

// forward compatibility
module.exports = Transmuxer;

},{"../codecs":23,"../m2ts/m2ts.js":30,"../utils/stream.js":41,"./flv-tag.js":24}],27:[function(_dereq_,module,exports){
'use strict';

var muxjs = {
  codecs: _dereq_('./codecs'),
  mp4: _dereq_('./mp4'),
  flv: _dereq_('./flv'),
  mp2t: _dereq_('./m2ts'),
};
module.exports = muxjs;

},{"./codecs":23,"./flv":25,"./m2ts":29,"./mp4":34}],28:[function(_dereq_,module,exports){
/**
 * mux.js
 *
 * Copyright (c) 2015 Brightcove
 * All rights reserved.
 *
 * Reads in-band caption information from a video elementary
 * stream. Captions must follow the CEA-708 standard for injection
 * into an MPEG-2 transport streams.
 * @see https://en.wikipedia.org/wiki/CEA-708
 */

'use strict';

// -----------------
// Link To Transport
// -----------------

// Supplemental enhancement information (SEI) NAL units have a
// payload type field to indicate how they are to be
// interpreted. CEAS-708 caption content is always transmitted with
// payload type 0x04.
var USER_DATA_REGISTERED_ITU_T_T35 = 4,
    RBSP_TRAILING_BITS = 128,
    Stream = _dereq_('../utils/stream'),
    codecs = _dereq_('../codecs');

/**
  * Parse a supplemental enhancement information (SEI) NAL unit.
  * Stops parsing once a message of type ITU T T35 has been found.
  *
  * @param bytes {Uint8Array} the bytes of a SEI NAL unit
  * @return {object} the parsed SEI payload
  * @see Rec. ITU-T H.264, 7.3.2.3.1
  */
var parseSei = function(bytes) {
  var
    i = 0,
    result = {
      payloadType: -1,
      payloadSize: 0,
    },
    payloadType = 0,
    payloadSize = 0;

  // go through the sei_rbsp parsing each each individual sei_message
  while (i < bytes.byteLength) {
    // stop once we have hit the end of the sei_rbsp
    if (bytes[i] === RBSP_TRAILING_BITS) {
      break;
    }

    // Parse payload type
    while (bytes[i] === 0xFF) {
      payloadType += 255;
      i++;
    }
    payloadType += bytes[i++];

    // Parse payload size
    while (bytes[i] === 0xFF) {
      payloadSize += 255;
      i++;
    }
    payloadSize += bytes[i++];

    // this sei_message is a 608/708 caption so save it and break
    // there can only ever be one caption message in a frame's sei
    if (!result.payload && payloadType === USER_DATA_REGISTERED_ITU_T_T35) {
      result.payloadType = payloadType;
      result.payloadSize = payloadSize;
      result.payload = bytes.subarray(i, i + payloadSize);
      break;
    }

    // skip the payload and parse the next message
    i += payloadSize;
    payloadType = 0;
    payloadSize = 0;
  }

  return result;
};

// see ANSI/SCTE 128-1 (2013), section 8.1
var parseUserData = function(sei) {
  // itu_t_t35_contry_code must be 181 (United States) for
  // captions
  if (sei.payload[0] !== 181) {
    return null;
  }

  // itu_t_t35_provider_code should be 49 (ATSC) for captions
  if (((sei.payload[1] << 8) | sei.payload[2]) !== 49) {
    return null;
  }

  // the user_identifier should be "GA94" to indicate ATSC1 data
  if (String.fromCharCode(sei.payload[3],
                          sei.payload[4],
                          sei.payload[5],
                          sei.payload[6]) !== 'GA94') {
    return null;
  }

  // finally, user_data_type_code should be 0x03 for caption data
  if (sei.payload[7] !== 0x03) {
    return null;
  }

  // return the user_data_type_structure and strip the trailing
  // marker bits
  return sei.payload.subarray(8, sei.payload.length - 1);
};

// see CEA-708-D, section 4.4
var parseCaptionPackets = function(pts, userData) {
  var results = [], i, count, offset, data;

  // if this is just filler, return immediately
  if (!(userData[0] & 0x40)) {
    return results;
  }

  // parse out the cc_data_1 and cc_data_2 fields
  count = userData[0] & 0x1f;
  for (i = 0; i < count; i++) {
    offset = i * 3;
    data = {
      type: userData[offset + 2] & 0x03,
      pts: pts
    };

    // capture cc data when cc_valid is 1
    if (userData[offset + 2] & 0x04) {
      data.ccData = (userData[offset + 3] << 8) | userData[offset + 4];
      results.push(data);
    }
  }
  return results;
};

var CaptionStream = function() {
  var self = this;
  CaptionStream.prototype.init.call(this);

  this.captionPackets_ = [];

  this.field1_ = new Cea608Stream();

  // forward data and done events from field1_ to this CaptionStream
  this.field1_.on('data', this.trigger.bind(this, 'data'));
  this.field1_.on('done', this.trigger.bind(this, 'done'));
};
CaptionStream.prototype = new Stream();
CaptionStream.prototype.push = function(event) {
  var sei, userData, captionPackets;

  // only examine SEI NALs
  if (event.nalUnitType !== codecs.h264.unitTypes.sei_rbsp) {
    return;
  }

  // parse the sei
  sei = parseSei(event.escapedRBSP);

  // ignore everything but user_data_registered_itu_t_t35
  if (sei.payloadType !== USER_DATA_REGISTERED_ITU_T_T35) {
    return;
  }

  // parse out the user data payload
  userData = parseUserData(sei);

  // ignore unrecognized userData
  if (!userData) {
    return;
  }

  // parse out CC data packets and save them for later
  this.captionPackets_ = this.captionPackets_.concat(parseCaptionPackets(event.pts, userData));
};

CaptionStream.prototype.flush = function () {
  // make sure we actually parsed captions before proceeding
  if (!this.captionPackets_.length) {
    this.field1_.flush();
    return;
  }

  // sort caption byte-pairs based on their PTS values
  this.captionPackets_.sort(function(a, b) {
    return a.pts - b.pts;
  });

  // Push each caption into Cea608Stream
  this.captionPackets_.forEach(this.field1_.push, this.field1_);

  this.captionPackets_.length = 0;
  this.field1_.flush();
  return;
};
// ----------------------
// Session to Application
// ----------------------

var BASIC_CHARACTER_TRANSLATION = {
  0x2a: 0xe1,
  0x5c: 0xe9,
  0x5e: 0xed,
  0x5f: 0xf3,
  0x60: 0xfa,
  0x7b: 0xe7,
  0x7c: 0xf7,
  0x7d: 0xd1,
  0x7e: 0xf1,
  0x7f: 0x2588
};

var getCharFromCode = function(code) {
  if(code === null) {
    return '';
  }
  code = BASIC_CHARACTER_TRANSLATION[code] || code;
  return String.fromCharCode(code);
};

// Constants for the byte codes recognized by Cea608Stream. This
// list is not exhaustive. For a more comprehensive listing and
// semantics see
// http://www.gpo.gov/fdsys/pkg/CFR-2010-title47-vol1/pdf/CFR-2010-title47-vol1-sec15-119.pdf
var PADDING                    = 0x0000,

    // Pop-on Mode
    RESUME_CAPTION_LOADING     = 0x1420,
    END_OF_CAPTION             = 0x142f,

    // Roll-up Mode
    ROLL_UP_2_ROWS             = 0x1425,
    ROLL_UP_3_ROWS             = 0x1426,
    ROLL_UP_4_ROWS             = 0x1427,
    RESUME_DIRECT_CAPTIONING   = 0x1429,
    CARRIAGE_RETURN            = 0x142d,
    // Erasure
    BACKSPACE                  = 0x1421,
    ERASE_DISPLAYED_MEMORY     = 0x142c,
    ERASE_NON_DISPLAYED_MEMORY = 0x142e;

// the index of the last row in a CEA-608 display buffer
var BOTTOM_ROW = 14;
// CEA-608 captions are rendered onto a 34x15 matrix of character
// cells. The "bottom" row is the last element in the outer array.
var createDisplayBuffer = function() {
  var result = [], i = BOTTOM_ROW + 1;
  while (i--) {
    result.push('');
  }
  return result;
};

var Cea608Stream = function() {
  Cea608Stream.prototype.init.call(this);

  this.mode_ = 'popOn';
  // When in roll-up mode, the index of the last row that will
  // actually display captions. If a caption is shifted to a row
  // with a lower index than this, it is cleared from the display
  // buffer
  this.topRow_ = 0;
  this.startPts_ = 0;
  this.displayed_ = createDisplayBuffer();
  this.nonDisplayed_ = createDisplayBuffer();
  this.lastControlCode_ = null;

  this.push = function(packet) {
    // Ignore other channels
    if (packet.type !== 0) {
      return;
    }
    var data, swap, char0, char1;
    // remove the parity bits
    data = packet.ccData & 0x7f7f;

    // ignore duplicate control codes
    if (data === this.lastControlCode_) {
      this.lastControlCode_ = null;
      return;
    }

    // Store control codes
    if ((data & 0xf000) === 0x1000) {
      this.lastControlCode_ = data;
    } else {
      this.lastControlCode_ = null;
    }

    switch (data) {
    case PADDING:
      break;
    case RESUME_CAPTION_LOADING:
      this.mode_ = 'popOn';
      break;
    case END_OF_CAPTION:
      // if a caption was being displayed, it's gone now
      this.flushDisplayed(packet.pts);

      // flip memory
      swap = this.displayed_;
      this.displayed_ = this.nonDisplayed_;
      this.nonDisplayed_ = swap;

      // start measuring the time to display the caption
      this.startPts_ = packet.pts;
      break;

    case ROLL_UP_2_ROWS:
      this.topRow_ = BOTTOM_ROW - 1;
      this.mode_ = 'rollUp';
      break;
    case ROLL_UP_3_ROWS:
      this.topRow_ = BOTTOM_ROW - 2;
      this.mode_ = 'rollUp';
      break;
    case ROLL_UP_4_ROWS:
      this.topRow_ = BOTTOM_ROW - 3;
      this.mode_ = 'rollUp';
      break;
    case CARRIAGE_RETURN:
      this.flushDisplayed(packet.pts);
      this.shiftRowsUp_();
      this.startPts_ = packet.pts;
      break;

    case BACKSPACE:
      if (this.mode_ === 'popOn') {
        this.nonDisplayed_[BOTTOM_ROW] = this.nonDisplayed_[BOTTOM_ROW].slice(0, -1);
      } else {
        this.displayed_[BOTTOM_ROW] = this.displayed_[BOTTOM_ROW].slice(0, -1);
      }
      break;
    case ERASE_DISPLAYED_MEMORY:
      this.flushDisplayed(packet.pts);
      this.displayed_ = createDisplayBuffer();
      break;
    case ERASE_NON_DISPLAYED_MEMORY:
      this.nonDisplayed_ = createDisplayBuffer();
      break;
    default:
      char0 = data >>> 8;
      char1 = data & 0xff;

      // Look for a Channel 1 Preamble Address Code
      if (char0 >= 0x10 && char0 <= 0x17 &&
          char1 >= 0x40 && char1 <= 0x7F &&
          (char0 !== 0x10 || char1 < 0x60)) {
        // Follow Safari's lead and replace the PAC with a space
        char0 = 0x20;
        // we only want one space so make the second character null
        // which will get become '' in getCharFromCode
        char1 = null;
      }

      // Look for special character sets
      if ((char0 === 0x11 || char0 === 0x19) &&
          (char1 >= 0x30 && char1 <= 0x3F)) {
        // Put in eigth note and space
        char0 = 0x266A;
        char1 = '';
      }

      // ignore unsupported control codes
      if ((char0 & 0xf0) === 0x10) {
        return;
      }

      // character handling is dependent on the current mode
      this[this.mode_](packet.pts, char0, char1);
      break;
    }
  };
};
Cea608Stream.prototype = new Stream();
// Trigger a cue point that captures the current state of the
// display buffer
Cea608Stream.prototype.flushDisplayed = function(pts) {
  var content = this.displayed_
    // remove spaces from the start and end of the string
    .map(function(row) { return row.trim(); })
    // remove empty rows
    .filter(function(row) { return row.length; })
    // combine all text rows to display in one cue
    .join('\n');

  if (content.length) {
    this.trigger('data', {
      startPts: this.startPts_,
      endPts: pts,
      text: content
    });
  }
};

// Mode Implementations
Cea608Stream.prototype.popOn = function(pts, char0, char1) {
  var baseRow = this.nonDisplayed_[BOTTOM_ROW];

  // buffer characters
  baseRow += getCharFromCode(char0);
  baseRow += getCharFromCode(char1);
  this.nonDisplayed_[BOTTOM_ROW] = baseRow;
};

Cea608Stream.prototype.rollUp = function(pts, char0, char1) {
  var baseRow = this.displayed_[BOTTOM_ROW];
  if (baseRow === '') {
    // we're starting to buffer new display input, so flush out the
    // current display
    this.flushDisplayed(pts);

    this.startPts_ = pts;
  }

  baseRow += getCharFromCode(char0);
  baseRow += getCharFromCode(char1);

  this.displayed_[BOTTOM_ROW] = baseRow;
};
Cea608Stream.prototype.shiftRowsUp_ = function() {
  var i;
  // clear out inactive rows
  for (i = 0; i < this.topRow_; i++) {
    this.displayed_[i] = '';
  }
  // shift displayed rows up
  for (i = this.topRow_; i < BOTTOM_ROW; i++) {
    this.displayed_[i] = this.displayed_[i + 1];
  }
  // clear out the bottom row
  this.displayed_[BOTTOM_ROW] = '';
};

// exports
module.exports = {
  CaptionStream: CaptionStream,
  Cea608Stream: Cea608Stream,
};


},{"../codecs":23,"../utils/stream":41}],29:[function(_dereq_,module,exports){
module.exports = _dereq_('./m2ts');

},{"./m2ts":30}],30:[function(_dereq_,module,exports){
/**
 * mux.js
 *
 * Copyright (c) 2015 Brightcove
 * All rights reserved.
 *
 * A stream-based mp2t to mp4 converter. This utility can be used to
 * deliver mp4s to a SourceBuffer on platforms that support native
 * Media Source Extensions.
 */
'use strict';
var Stream = _dereq_('../utils/stream.js'),
  CaptionStream = _dereq_('./caption-stream'),
  StreamTypes = _dereq_('./stream-types'),
  TimestampRolloverStream = _dereq_('./timestamp-rollover-stream').TimestampRolloverStream;

var m2tsStreamTypes = _dereq_('./stream-types.js');

// object types
var TransportPacketStream, TransportParseStream, ElementaryStream;

// constants
var
  MP2T_PACKET_LENGTH = 188, // bytes
  SYNC_BYTE = 0x47;

/**
 * Splits an incoming stream of binary data into MPEG-2 Transport
 * Stream packets.
 */
TransportPacketStream = function() {
  var
    buffer = new Uint8Array(MP2T_PACKET_LENGTH),
    bytesInBuffer = 0;

  TransportPacketStream.prototype.init.call(this);

   // Deliver new bytes to the stream.

  this.push = function(bytes) {
    var
      startIndex = 0,
      endIndex = MP2T_PACKET_LENGTH,
      everything;

    // If there are bytes remaining from the last segment, prepend them to the
    // bytes that were pushed in
    if (bytesInBuffer) {
      everything = new Uint8Array(bytes.byteLength + bytesInBuffer);
      everything.set(buffer.subarray(0, bytesInBuffer));
      everything.set(bytes, bytesInBuffer);
      bytesInBuffer = 0;
    } else {
      everything = bytes;
    }

    // While we have enough data for a packet
    while (endIndex < everything.byteLength) {
      // Look for a pair of start and end sync bytes in the data..
      if (everything[startIndex] === SYNC_BYTE && everything[endIndex] === SYNC_BYTE) {
        // We found a packet so emit it and jump one whole packet forward in
        // the stream
        this.trigger('data', everything.subarray(startIndex, endIndex));
        startIndex += MP2T_PACKET_LENGTH;
        endIndex += MP2T_PACKET_LENGTH;
        continue;
      }
      // If we get here, we have somehow become de-synchronized and we need to step
      // forward one byte at a time until we find a pair of sync bytes that denote
      // a packet
      startIndex++;
      endIndex++;
    }

    // If there was some data left over at the end of the segment that couldn't
    // possibly be a whole packet, keep it because it might be the start of a packet
    // that continues in the next segment
    if (startIndex < everything.byteLength) {
      buffer.set(everything.subarray(startIndex), 0);
      bytesInBuffer = everything.byteLength - startIndex;
    }
  };

  this.flush = function() {
    // If the buffer contains a whole packet when we are being flushed, emit it
    // and empty the buffer. Otherwise hold onto the data because it may be
    // important for decoding the next segment
    if (bytesInBuffer === MP2T_PACKET_LENGTH && buffer[0] === SYNC_BYTE) {
      this.trigger('data', buffer);
      bytesInBuffer = 0;
    }
    this.trigger('done');
  };
};
TransportPacketStream.prototype = new Stream();

/**
 * Accepts an MP2T TransportPacketStream and emits data events with parsed
 * forms of the individual transport stream packets.
 */
TransportParseStream = function() {
  var parsePsi, parsePat, parsePmt, parsePes, self;
  TransportParseStream.prototype.init.call(this);
  self = this;

  this.packetsWaitingForPmt = [];
  this.programMapTable = undefined;

  parsePsi = function(payload, psi) {
    var offset = 0;

    // PSI packets may be split into multiple sections and those
    // sections may be split into multiple packets. If a PSI
    // section starts in this packet, the payload_unit_start_indicator
    // will be true and the first byte of the payload will indicate
    // the offset from the current position to the start of the
    // section.
    if (psi.payloadUnitStartIndicator) {
      offset += payload[offset] + 1;
    }

    if (psi.type === 'pat') {
      parsePat(payload.subarray(offset), psi);
    } else {
      parsePmt(payload.subarray(offset), psi);
    }
  };

  parsePat = function(payload, pat) {
    pat.section_number = payload[7];
    pat.last_section_number = payload[8];

    // skip the PSI header and parse the first PMT entry
    self.pmtPid = (payload[10] & 0x1F) << 8 | payload[11];
    pat.pmtPid = self.pmtPid;
  };

  /**
   * Parse out the relevant fields of a Program Map Table (PMT).
   * @param payload {Uint8Array} the PMT-specific portion of an MP2T
   * packet. The first byte in this array should be the table_id
   * field.
   * @param pmt {object} the object that should be decorated with
   * fields parsed from the PMT.
   */
  parsePmt = function(payload, pmt) {
    var sectionLength, tableEnd, programInfoLength, offset;

    // PMTs can be sent ahead of the time when they should actually
    // take effect. We don't believe this should ever be the case
    // for HLS but we'll ignore "forward" PMT declarations if we see
    // them. Future PMT declarations have the current_next_indicator
    // set to zero.
    if (!(payload[5] & 0x01)) {
      return;
    }

    // overwrite any existing program map table
    self.programMapTable = {};

    // the mapping table ends at the end of the current section
    sectionLength = (payload[1] & 0x0f) << 8 | payload[2];
    tableEnd = 3 + sectionLength - 4;

    // to determine where the table is, we have to figure out how
    // long the program info descriptors are
    programInfoLength = (payload[10] & 0x0f) << 8 | payload[11];

    // advance the offset to the first entry in the mapping table
    offset = 12 + programInfoLength;
    while (offset < tableEnd) {
      // add an entry that maps the elementary_pid to the stream_type
      self.programMapTable[(payload[offset + 1] & 0x1F) << 8 | payload[offset + 2]] = payload[offset];

      // move to the next table entry
      // skip past the elementary stream descriptors, if present
      offset += ((payload[offset + 3] & 0x0F) << 8 | payload[offset + 4]) + 5;
    }

    // record the map on the packet as well
    pmt.programMapTable = self.programMapTable;

    // if there are any packets waiting for a PMT to be found, process them now
    while (self.packetsWaitingForPmt.length) {
      self.processPes_.apply(self, self.packetsWaitingForPmt.shift());
    }
  };

  /**
   * Deliver a new MP2T packet to the stream.
   */
  this.push = function(packet) {
    var
      result = {},
      offset = 4;

    result.payloadUnitStartIndicator = !!(packet[1] & 0x40);

    // pid is a 13-bit field starting at the last bit of packet[1]
    result.pid = packet[1] & 0x1f;
    result.pid <<= 8;
    result.pid |= packet[2];

    // if an adaption field is present, its length is specified by the
    // fifth byte of the TS packet header. The adaptation field is
    // used to add stuffing to PES packets that don't fill a complete
    // TS packet, and to specify some forms of timing and control data
    // that we do not currently use.
    if (((packet[3] & 0x30) >>> 4) > 0x01) {
      offset += packet[offset] + 1;
    }

    // parse the rest of the packet based on the type
    if (result.pid === 0) {
      result.type = 'pat';
      parsePsi(packet.subarray(offset), result);
      this.trigger('data', result);
    } else if (result.pid === this.pmtPid) {
      result.type = 'pmt';
      parsePsi(packet.subarray(offset), result);
      this.trigger('data', result);
    } else if (this.programMapTable === undefined) {
      // When we have not seen a PMT yet, defer further processing of
      // PES packets until one has been parsed
      this.packetsWaitingForPmt.push([packet, offset, result]);
    } else {
      this.processPes_(packet, offset, result);
    }
  };

  this.processPes_ = function(packet, offset, result) {
    result.streamType = this.programMapTable[result.pid];
    result.type = 'pes';
    result.data = packet.subarray(offset);

    this.trigger('data', result);
  };

};
TransportParseStream.prototype = new Stream();
TransportParseStream.STREAM_TYPES  = {
  h264: 0x1b,
  adts: 0x0f
};

/**
 * Reconsistutes program elementary stream (PES) packets from parsed
 * transport stream packets. That is, if you pipe an
 * mp2t.TransportParseStream into a mp2t.ElementaryStream, the output
 * events will be events which capture the bytes for individual PES
 * packets plus relevant metadata that has been extracted from the
 * container.
 */
ElementaryStream = function() {
  var
    self = this,
    // PES packet fragments
    video = {
      data: [],
      size: 0
    },
    audio = {
      data: [],
      size: 0
    },
    timedMetadata = {
      data: [],
      size: 0
    },
    parsePes = function(payload, pes) {
      var ptsDtsFlags;
      function extract_ts(arr, index) {
        var ts = (arr[index] & 0x0E) * 536870912 + // 1 << 29
          (arr[index+1] & 0xFF) * 4194304 + // 1 << 22
          (arr[index+2] & 0xFE) * 16384 + // 1 << 14
          (arr[index+3] & 0xFF) * 128 + // 1 << 7
          (arr[index+4] & 0xFE) / 2;
        // check if greater than 2^32 -1
        if (ts > 4294967295) {
          // decrement 2^33
          ts -= 8589934592;
        }
        return ts;
      }

      // find out if this packets starts a new keyframe
      pes.dataAlignmentIndicator = (payload[6] & 0x04) !== 0;
      // PES packets may be annotated with a PTS value, or a PTS value
      // and a DTS value. Determine what combination of values is
      // available to work with.
      ptsDtsFlags = payload[7];

      // PTS and DTS are normally stored as a 33-bit number.  Javascript
      // performs all bitwise operations on 32-bit integers but javascript
      // supports a much greater range (52-bits) of integer using standard
      // mathematical operations.
      // We construct a 31-bit value using bitwise operators over the 31
      // most significant bits and then multiply by 4 (equal to a left-shift
      // of 2) before we add the final 2 least significant bits of the
      // timestamp (equal to an OR.)
      if (ptsDtsFlags & 0xC0) {
        // the PTS and DTS are not written out directly. For information
        // on how they are encoded, see
        // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
        pes.pts = extract_ts(payload, 9);
        pes.dts = ptsDtsFlags & 0x40 ? extract_ts(payload, 14) : pes.pts;
      }
      // the data section starts immediately after the PES header.
      // pes_header_data_length specifies the number of header bytes
      // that follow the last byte of the field.
      pes.data = payload.subarray(9 + payload[8]);
    },
    flushStream = function(stream, type) {
      var
        packetData = new Uint8Array(stream.size),
        event = {
          type: type
        },
        i = 0,
        fragment;

      // do nothing if there is no buffered data
      if (!stream.data.length) {
        return;
      }
      event.trackId = stream.data[0].pid;

      // reassemble the packet
      while (stream.data.length) {
        fragment = stream.data.shift();

        packetData.set(fragment.data, i);
        i += fragment.data.byteLength;
      }

      // parse assembled packet's PES header
      parsePes(packetData, event);

      stream.size = 0;

      self.trigger('data', event);
    };

  ElementaryStream.prototype.init.call(this);

  this.push = function(data) {
    ({
      pat: function() {
        // we have to wait for the PMT to arrive as well before we
        // have any meaningful metadata
      },
      pes: function() {
        var stream, streamType;

        switch (data.streamType) {
        case StreamTypes.H264_STREAM_TYPE:
        case m2tsStreamTypes.H264_STREAM_TYPE:
          stream = video;
          streamType = 'video';
          break;
        case StreamTypes.ADTS_STREAM_TYPE:
          stream = audio;
          streamType = 'audio';
          break;
        case StreamTypes.METADATA_STREAM_TYPE:
          stream = timedMetadata;
          streamType = 'timed-metadata';
          break;
        default:
          // ignore unknown stream types
          return;
        }

        // if a new packet is starting, we can flush the completed
        // packet
        if (data.payloadUnitStartIndicator) {
          flushStream(stream, streamType);
        }

        // buffer this fragment until we are sure we've received the
        // complete payload
        stream.data.push(data);
        stream.size += data.data.byteLength;
      },
      pmt: function() {
        var
          event = {
            type: 'metadata',
            tracks: []
          },
          programMapTable = data.programMapTable,
          k,
          track;

        // translate streams to tracks
        for (k in programMapTable) {
          if (programMapTable.hasOwnProperty(k)) {
            track = {
              timelineStartInfo: {
                baseMediaDecodeTime: 0
              }
            };
            track.id = +k;
            if (programMapTable[k] === m2tsStreamTypes.H264_STREAM_TYPE) {
              track.codec = 'avc';
              track.type = 'video';
            } else if (programMapTable[k] === m2tsStreamTypes.ADTS_STREAM_TYPE) {
              track.codec = 'adts';
              track.type = 'audio';
            }
            event.tracks.push(track);
          }
        }
        self.trigger('data', event);
      }
    })[data.type]();
  };

  /**
   * Flush any remaining input. Video PES packets may be of variable
   * length. Normally, the start of a new video packet can trigger the
   * finalization of the previous packet. That is not possible if no
   * more video is forthcoming, however. In that case, some other
   * mechanism (like the end of the file) has to be employed. When it is
   * clear that no additional data is forthcoming, calling this method
   * will flush the buffered packets.
   */
  this.flush = function() {
    // !!THIS ORDER IS IMPORTANT!!
    // video first then audio
    flushStream(video, 'video');
    flushStream(audio, 'audio');
    flushStream(timedMetadata, 'timed-metadata');
    this.trigger('done');
  };
};
ElementaryStream.prototype = new Stream();

var m2ts = {
  PAT_PID: 0x0000,
  MP2T_PACKET_LENGTH: MP2T_PACKET_LENGTH,
  TransportPacketStream: TransportPacketStream,
  TransportParseStream: TransportParseStream,
  ElementaryStream: ElementaryStream,
  TimestampRolloverStream: TimestampRolloverStream,
  CaptionStream: CaptionStream.CaptionStream,
  Cea608Stream: CaptionStream.Cea608Stream,
  MetadataStream: _dereq_('./metadata-stream')
};

for (var type in StreamTypes) {
  if (StreamTypes.hasOwnProperty(type)) {
    m2ts[type] = StreamTypes[type];
  }
}

module.exports = m2ts;

},{"../utils/stream.js":41,"./caption-stream":28,"./metadata-stream":31,"./stream-types":32,"./stream-types.js":32,"./timestamp-rollover-stream":33}],31:[function(_dereq_,module,exports){
/**
 * Accepts program elementary stream (PES) data events and parses out
 * ID3 metadata from them, if present.
 * @see http://id3.org/id3v2.3.0
 */
'use strict';
var
  Stream = _dereq_('../utils/stream'),
  StreamTypes = _dereq_('./stream-types'),
  // return a percent-encoded representation of the specified byte range
  // @see http://en.wikipedia.org/wiki/Percent-encoding
  percentEncode = function(bytes, start, end) {
    var i, result = '';
    for (i = start; i < end; i++) {
      result += '%' + ('00' + bytes[i].toString(16)).slice(-2);
    }
    return result;
  },
  // return the string representation of the specified byte range,
  // interpreted as UTf-8.
  parseUtf8 = function(bytes, start, end) {
    return decodeURIComponent(percentEncode(bytes, start, end));
  },
  // return the string representation of the specified byte range,
  // interpreted as ISO-8859-1.
  parseIso88591 = function(bytes, start, end) {
    return unescape(percentEncode(bytes, start, end)); // jshint ignore:line
  },
  parseSyncSafeInteger = function (data) {
    return (data[0] << 21) |
            (data[1] << 14) |
            (data[2] << 7) |
            (data[3]);
  },
  tagParsers = {
    'TXXX': function(tag) {
      var i;
      if (tag.data[0] !== 3) {
        // ignore frames with unrecognized character encodings
        return;
      }

      for (i = 1; i < tag.data.length; i++) {
        if (tag.data[i] === 0) {
          // parse the text fields
          tag.description = parseUtf8(tag.data, 1, i);
          // do not include the null terminator in the tag value
          tag.value = parseUtf8(tag.data, i + 1, tag.data.length - 1);
          break;
        }
      }
      tag.data = tag.value;
    },
    'WXXX': function(tag) {
      var i;
      if (tag.data[0] !== 3) {
        // ignore frames with unrecognized character encodings
        return;
      }

      for (i = 1; i < tag.data.length; i++) {
        if (tag.data[i] === 0) {
          // parse the description and URL fields
          tag.description = parseUtf8(tag.data, 1, i);
          tag.url = parseUtf8(tag.data, i + 1, tag.data.length);
          break;
        }
      }
    },
    'PRIV': function(tag) {
      var i;

      for (i = 0; i < tag.data.length; i++) {
        if (tag.data[i] === 0) {
          // parse the description and URL fields
          tag.owner = parseIso88591(tag.data, 0, i);
          break;
        }
      }
      tag.privateData = tag.data.subarray(i + 1);
      tag.data = tag.privateData;
    }
  },
  MetadataStream;

MetadataStream = function(options) {
  var
    settings = {
      debug: !!(options && options.debug),

      // the bytes of the program-level descriptor field in MP2T
      // see ISO/IEC 13818-1:2013 (E), section 2.6 "Program and
      // program element descriptors"
      descriptor: options && options.descriptor
    },
    // the total size in bytes of the ID3 tag being parsed
    tagSize = 0,
    // tag data that is not complete enough to be parsed
    buffer = [],
    // the total number of bytes currently in the buffer
    bufferSize = 0,
    i;

  MetadataStream.prototype.init.call(this);

  // calculate the text track in-band metadata track dispatch type
  // https://html.spec.whatwg.org/multipage/embedded-content.html#steps-to-expose-a-media-resource-specific-text-track
  this.dispatchType = StreamTypes.METADATA_STREAM_TYPE.toString(16);
  if (settings.descriptor) {
    for (i = 0; i < settings.descriptor.length; i++) {
      this.dispatchType += ('00' + settings.descriptor[i].toString(16)).slice(-2);
    }
  }

  this.push = function(chunk) {
    var tag, frameStart, frameSize, frame, i, frameHeader;
    if (chunk.type !== 'timed-metadata') {
      return;
    }

    // if data_alignment_indicator is set in the PES header,
    // we must have the start of a new ID3 tag. Assume anything
    // remaining in the buffer was malformed and throw it out
    if (chunk.dataAlignmentIndicator) {
      bufferSize = 0;
      buffer.length = 0;
    }

    // ignore events that don't look like ID3 data
    if (buffer.length === 0 &&
        (chunk.data.length < 10 ||
          chunk.data[0] !== 'I'.charCodeAt(0) ||
          chunk.data[1] !== 'D'.charCodeAt(0) ||
          chunk.data[2] !== '3'.charCodeAt(0))) {
      if (settings.debug) {
        console.log('Skipping unrecognized metadata packet');
      }
      return;
    }

    // add this chunk to the data we've collected so far

    buffer.push(chunk);
    bufferSize += chunk.data.byteLength;

    // grab the size of the entire frame from the ID3 header
    if (buffer.length === 1) {
      // the frame size is transmitted as a 28-bit integer in the
      // last four bytes of the ID3 header.
      // The most significant bit of each byte is dropped and the
      // results concatenated to recover the actual value.
      tagSize = parseSyncSafeInteger(chunk.data.subarray(6, 10));

      // ID3 reports the tag size excluding the header but it's more
      // convenient for our comparisons to include it
      tagSize += 10;
    }

    // if the entire frame has not arrived, wait for more data
    if (bufferSize < tagSize) {
      return;
    }

    // collect the entire frame so it can be parsed
    tag = {
      data: new Uint8Array(tagSize),
      frames: [],
      pts: buffer[0].pts,
      dts: buffer[0].dts
    };
    for (i = 0; i < tagSize;) {
      tag.data.set(buffer[0].data.subarray(0, tagSize - i), i);
      i += buffer[0].data.byteLength;
      bufferSize -= buffer[0].data.byteLength;
      buffer.shift();
    }

    // find the start of the first frame and the end of the tag
    frameStart = 10;
    if (tag.data[5] & 0x40) {
      // advance the frame start past the extended header
      frameStart += 4; // header size field
      frameStart += parseSyncSafeInteger(tag.data.subarray(10, 14));

      // clip any padding off the end
      tagSize -= parseSyncSafeInteger(tag.data.subarray(16, 20));
    }

    // parse one or more ID3 frames
    // http://id3.org/id3v2.3.0#ID3v2_frame_overview
    do {
      // determine the number of bytes in this frame
      frameSize = parseSyncSafeInteger(tag.data.subarray(frameStart + 4, frameStart + 8));
      if (frameSize < 1) {
        return console.log('Malformed ID3 frame encountered. Skipping metadata parsing.');
      }
      frameHeader = String.fromCharCode(tag.data[frameStart],
                                        tag.data[frameStart + 1],
                                        tag.data[frameStart + 2],
                                        tag.data[frameStart + 3]);


      frame = {
        id: frameHeader,
        data: tag.data.subarray(frameStart + 10, frameStart + frameSize + 10)
      };
      frame.key = frame.id;
      if (tagParsers[frame.id]) {
        tagParsers[frame.id](frame);
        if (frame.owner === 'com.apple.streaming.transportStreamTimestamp') {
          var
            d = frame.data,
            size = ((d[3] & 0x01)  << 30) |
                   (d[4]  << 22) |
                   (d[5] << 14) |
                   (d[6] << 6) |
                   (d[7] >>> 2);

          size *= 4;
          size += d[7] & 0x03;
          frame.timeStamp = size;
          this.trigger('timestamp', frame);
        }
      }
      tag.frames.push(frame);

      frameStart += 10; // advance past the frame header
      frameStart += frameSize; // advance past the frame body
    } while (frameStart < tagSize);
    this.trigger('data', tag);
  };
};
MetadataStream.prototype = new Stream();

module.exports = MetadataStream;

},{"../utils/stream":41,"./stream-types":32}],32:[function(_dereq_,module,exports){
'use strict';

module.exports = {
  H264_STREAM_TYPE: 0x1B,
  ADTS_STREAM_TYPE: 0x0F,
  METADATA_STREAM_TYPE: 0x15
};

},{}],33:[function(_dereq_,module,exports){
/**
 * mux.js
 *
 * Copyright (c) 2016 Brightcove
 * All rights reserved.
 *
 * Accepts program elementary stream (PES) data events and corrects
 * decode and presentation time stamps to account for a rollover
 * of the 33 bit value.
 */

'use strict';

var Stream = _dereq_('../utils/stream');

var MAX_TS = 8589934592;

var RO_THRESH = 4294967296;

var handleRollover = function(value, reference) {
  var direction = 1;

  if (value > reference) {
    // If the current timestamp value is greater than our reference timestamp and we detect a
    // timestamp rollover, this means the roll over is happening in the opposite direction.
    // Example scenario: Enter a long stream/video just after a rollover occurred. The reference
    // point will be set to a small number, e.g. 1. The user then seeks backwards over the
    // rollover point. In loading this segment, the timestamp values will be very large,
    // e.g. 2^33 - 1. Since this comes before the data we loaded previously, we want to adjust
    // the time stamp to be `value - 2^33`.
    direction = -1;
  }

  // Note: A seek forwards or back that is greater than the RO_THRESH (2^32, ~13 hours) will
  // cause an incorrect adjustment.
  while (Math.abs(reference - value) > RO_THRESH) {
    value += (direction * MAX_TS);
  }

  return value;
};

var TimestampRolloverStream = function(type) {
  var lastDTS, referenceDTS;

  TimestampRolloverStream.prototype.init.call(this);

  this.type_ = type;

  this.push = function(data) {
    if (data.type !== this.type_) {
      return;
    }

    if (referenceDTS === undefined) {
      referenceDTS = data.dts;
    }

    data.dts = handleRollover(data.dts, referenceDTS);
    data.pts = handleRollover(data.pts, referenceDTS);

    lastDTS = data.dts;

    this.trigger('data', data);
  };

  this.flush = function() {
    referenceDTS = lastDTS;
    this.trigger('done');
  };

};

TimestampRolloverStream.prototype = new Stream();

module.exports = {
  TimestampRolloverStream: TimestampRolloverStream,
  handleRollover: handleRollover
};

},{"../utils/stream":41}],34:[function(_dereq_,module,exports){
module.exports = {
  generator: _dereq_('./mp4-generator'),
  Transmuxer: _dereq_('./transmuxer').Transmuxer,
  AudioSegmentStream: _dereq_('./transmuxer').AudioSegmentStream,
  VideoSegmentStream: _dereq_('./transmuxer').VideoSegmentStream,
  tools: _dereq_('../tools/mp4-inspector'),
  MP4ParserStream: _dereq_('./mp4-parser').MP4ParserStream,
  MP4BuilderStream: _dereq_('./mp4-parser').MP4BuilderStream,
};

},{"../tools/mp4-inspector":39,"./mp4-generator":35,"./mp4-parser":36,"./transmuxer":37}],35:[function(_dereq_,module,exports){
/**
 * mux.js
 *
 * Copyright (c) 2015 Brightcove
 * All rights reserved.
 *
 * Functions that generate fragmented MP4s suitable for use with Media
 * Source Extensions.
 */
'use strict';

var UINT32_MAX = Math.pow(2, 32) - 1;
var box, cslg, dinf, esds, ftyp, edts, elst, mdat, mfhd, minf, moof, moov,
    mvex, mvhd, trak, tkhd, mdia, mdhd, hdlr, sdtp, stbl, stsd, styp, traf,
    trep, trex, trun, types, MAJOR_BRAND, MINOR_VERSION, AVC1_BRAND,
    VIDEO_HDLR, AUDIO_HDLR, HDLR_TYPES, VMHD, SMHD, DREF, STCO, STSC, STSZ,
    STTS, Uint8Array, DataView;

Uint8Array = window.Uint8Array;
DataView = window.DataView;

// pre-calculate constants
(function() {
  var i;
  types = {
    avc1: [], // codingname
    avcC: [],
    btrt: [],
    cslg: [],
    dinf: [],
    dref: [],
    edts: [],
    elst: [],
    esds: [],
    ftyp: [],
    hdlr: [],
    mdat: [],
    mdhd: [],
    mdia: [],
    mfhd: [],
    minf: [],
    moof: [],
    moov: [],
    mp4a: [], // codingname
    mvex: [],
    mvhd: [],
    sdtp: [],
    smhd: [],
    stbl: [],
    stco: [],
    stsc: [],
    stsd: [],
    stsz: [],
    stts: [],
    styp: [],
    tfdt: [],
    tfhd: [],
    traf: [],
    trak: [],
    trun: [],
    trep: [],
    trex: [],
    tkhd: [],
    vmhd: []
  };

  // In environments where Uint8Array is undefined (e.g., IE8), skip set up so that we
  // don't throw an error
  if (typeof Uint8Array === 'undefined') {
    return;
  }

  for (i in types) {
    if (types.hasOwnProperty(i)) {
      types[i] = [
        i.charCodeAt(0),
        i.charCodeAt(1),
        i.charCodeAt(2),
        i.charCodeAt(3)
      ];
    }
  }

  MAJOR_BRAND = new Uint8Array([
    'i'.charCodeAt(0),
    's'.charCodeAt(0),
    'o'.charCodeAt(0),
    'm'.charCodeAt(0)
  ]);
  AVC1_BRAND = new Uint8Array([
    'a'.charCodeAt(0),
    'v'.charCodeAt(0),
    'c'.charCodeAt(0),
    '1'.charCodeAt(0)
  ]);
  MINOR_VERSION = new Uint8Array([0, 0, 0, 1]);
  VIDEO_HDLR = new Uint8Array([
    0x00, // version 0
    0x00, 0x00, 0x00, // flags
    0x00, 0x00, 0x00, 0x00, // pre_defined
    0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
    0x00, 0x00, 0x00, 0x00, // reserved
    0x00, 0x00, 0x00, 0x00, // reserved
    0x00, 0x00, 0x00, 0x00, // reserved
    0x56, 0x69, 0x64, 0x65,
    0x6f, 0x48, 0x61, 0x6e,
    0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
  ]);
  AUDIO_HDLR = new Uint8Array([
    0x00, // version 0
    0x00, 0x00, 0x00, // flags
    0x00, 0x00, 0x00, 0x00, // pre_defined
    0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
    0x00, 0x00, 0x00, 0x00, // reserved
    0x00, 0x00, 0x00, 0x00, // reserved
    0x00, 0x00, 0x00, 0x00, // reserved
    0x53, 0x6f, 0x75, 0x6e,
    0x64, 0x48, 0x61, 0x6e,
    0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
  ]);
  HDLR_TYPES = {
    video: VIDEO_HDLR,
    audio: AUDIO_HDLR
  };
  DREF = new Uint8Array([
    0x00, // version 0
    0x00, 0x00, 0x00, // flags
    0x00, 0x00, 0x00, 0x01, // entry_count
    0x00, 0x00, 0x00, 0x0c, // entry_size
    0x75, 0x72, 0x6c, 0x20, // 'url' type
    0x00, // version 0
    0x00, 0x00, 0x01 // entry_flags
  ]);
  SMHD = new Uint8Array([
    0x00,             // version
    0x00, 0x00, 0x00, // flags
    0x00, 0x00,       // balance, 0 means centered
    0x00, 0x00        // reserved
  ]);
  STCO = new Uint8Array([
    0x00, // version
    0x00, 0x00, 0x00, // flags
    0x00, 0x00, 0x00, 0x00 // entry_count
  ]);
  STSC = STCO;
  STSZ = new Uint8Array([
    0x00, // version
    0x00, 0x00, 0x00, // flags
    0x00, 0x00, 0x00, 0x00, // sample_size
    0x00, 0x00, 0x00, 0x00, // sample_count
  ]);
  STTS = STCO;
  VMHD = new Uint8Array([
    0x00, // version
    0x00, 0x00, 0x01, // flags
    0x00, 0x00, // graphicsmode
    0x00, 0x00,
    0x00, 0x00,
    0x00, 0x00 // opcolor
  ]);
}());

function uint32_to_arr(num){
  return [num>>>24&255, num>>>16&255, num>>>8&255, num&255];
}

function uint16_to_arr(num){
  return [num>>>8&255, num&255];
}

box = function(type) {
  var
    payload = [],
    size = 0,
    i,
    result,
    view;

  for (i = 1; i < arguments.length; i++) {
    payload.push(arguments[i]);
  }

  i = payload.length;

  // calculate the total size we need to allocate
  while (i--) {
    size += payload[i].byteLength;
  }
  result = new Uint8Array(size + 8);
  view = new DataView(result.buffer, result.byteOffset, result.byteLength);
  view.setUint32(0, result.byteLength);
  result.set(type, 4);

  // copy the payload into the result
  for (i = 0, size = 8; i < payload.length; i++) {
    result.set(payload[i], size);
    size += payload[i].byteLength;
  }
  return result;
};

cslg = function(cslg) {
  var obj = {};
  for (var k in cslg)
      obj[k] = cslg[k]>>>0;
  return box(types.cslg, new Uint8Array([
    0x00, // version
    0x00, 0x00, 0x00, // flags
    (obj.ctts_shift >>> 24) & 0xFF,
    (obj.ctts_shift >>> 16) & 0xFF,
    (obj.ctts_shift >>>  8) & 0xFF,
    obj.ctts_shift & 0xFF,
    (obj.min_ctts >>> 24) & 0xFF,
    (obj.min_ctts >>> 16) & 0xFF,
    (obj.min_ctts >>>  8) & 0xFF,
    obj.min_ctts & 0xFF,
    (obj.max_ctts >>> 24) & 0xFF,
    (obj.max_ctts >>> 16) & 0xFF,
    (obj.max_ctts >>>  8) & 0xFF,
    obj.max_ctts & 0xFF,
    (obj.min_cts >>> 24) & 0xFF,
    (obj.min_cts >>> 16) & 0xFF,
    (obj.min_cts >>>  8) & 0xFF,
    obj.min_cts & 0xFF,
    (obj.max_cts >>> 24) & 0xFF,
    (obj.max_cts >>> 16) & 0xFF,
    (obj.max_cts >>>  8) & 0xFF,
    obj.max_cts & 0xFF,
  ]));
};

dinf = function() {
  return box(types.dinf, box(types.dref, DREF));
};

edts = function(track) {
  return box(types.edts, elst(track));
};

elst = function(track) {
  var count = track.edit_list.length, i;
  var bytes = [
    0x00, // version
    0x00, 0x00, 0x00
  ].concat(uint32_to_arr(count)); // entries count
  for (i = 0; i < count; i++)
  {
    bytes = bytes.concat(uint32_to_arr(track.edit_list[i].segment_duration))
    .concat(uint32_to_arr(track.edit_list[i].media_time))
    .concat(uint16_to_arr(track.edit_list[i].media_rate))
    .concat(uint16_to_arr(0)); // reserved
  }
  return box(types.elst, new Uint8Array(bytes));
};

esds = function(track) {
  return box(types.esds, new Uint8Array([
    0x00, // version
    0x00, 0x00, 0x00, // flags

    // ES_Descriptor
    0x03, // tag, ES_DescrTag
    0x19, // length
    0x00, 0x00, // ES_ID
    0x00, // streamDependenceFlag, URL_flag, reserved, streamPriority

    // DecoderConfigDescriptor
    0x04, // tag, DecoderConfigDescrTag
    0x11, // length
    0x40, // object type
    0x15,  // streamType
    0x00, 0x06, 0x00, // bufferSizeDB
    0x00, 0x00, 0xda, 0xc0, // maxBitrate
    0x00, 0x00, 0xda, 0xc0, // avgBitrate

    // DecoderSpecificInfo
    0x05, // tag, DecoderSpecificInfoTag
    0x02, // length
    // ISO/IEC 14496-3, AudioSpecificConfig
    // for samplingFrequencyIndex see ISO/IEC 13818-7:2006, 8.1.3.2.2, Table 35
    (track.audioobjecttype << 3) | (track.samplingfrequencyindex >>> 1),
    (track.samplingfrequencyindex << 7) | (track.channelcount << 3),
    0x06, 0x01, 0x02 // GASpecificConfig
  ]));
};

ftyp = function(opt) {
    opt = opt||{};
    var params = opt.compatible||[MAJOR_BRAND, AVC1_BRAND];
    params = params.slice(0);
    params.unshift(types.ftyp, opt.major||MAJOR_BRAND, MINOR_VERSION);
    return box.apply(null, params);
};

hdlr = function(type) {
  return box(types.hdlr, HDLR_TYPES[type]);
};
mdat = function(data) {
  return box(types.mdat, data);
};
mdhd = function(track) {
  var result = new Uint8Array([
    0x00,                   // version 0
    0x00, 0x00, 0x00,       // flags
    0x00, 0x00, 0x00, 0x02, // creation_time
    0x00, 0x00, 0x00, 0x03, // modification_time
    0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second

    (track.duration >>> 24) & 0xFF,
    (track.duration >>> 16) & 0xFF,
    (track.duration >>>  8) & 0xFF,
    track.duration & 0xFF,  // duration
    0x55, 0xc4,             // 'und' language (undetermined)
    0x00, 0x00
  ]);

  // Use the sample rate from the track metadata, when it is
  // defined. The sample rate can be parsed out of an ADTS header, for
  // instance.
  if (track.samplerate) {
    result[12] = (track.samplerate >>> 24) & 0xFF;
    result[13] = (track.samplerate >>> 16) & 0xFF;
    result[14] = (track.samplerate >>>  8) & 0xFF;
    result[15] = (track.samplerate)        & 0xFF;
  }

  return box(types.mdhd, result);
};
mdia = function(track) {
  return box(types.mdia, mdhd(track), hdlr(track.type), minf(track));
};
mfhd = function(sequenceNumber) {
  return box(types.mfhd, new Uint8Array([
    0x00,
    0x00, 0x00, 0x00, // flags
    (sequenceNumber & 0xFF000000) >> 24,
    (sequenceNumber & 0xFF0000) >> 16,
    (sequenceNumber & 0xFF00) >> 8,
    sequenceNumber & 0xFF, // sequence_number
  ]));
};
minf = function(track) {
  return box(types.minf,
    track.type === 'video' ? box(types.vmhd, VMHD) : box(types.smhd, SMHD),
    dinf(), stbl(track));
};
moof = function(sequenceNumber, tracks, opt) {
  var
    trackFragments = [],
    i = tracks.length;
  opt = opt||{};
  // build traf boxes for each track fragment
  while (i--) {
    trackFragments[i] = traf(tracks[i], opt);
  }
  return box.apply(null, [
    types.moof,
    mfhd(sequenceNumber)
  ].concat(trackFragments));
};
/**
 * Returns a movie box.
 * @param tracks {array} the tracks associated with this movie
 * @see ISO/IEC 14496-12:2012(E), section 8.2.1
 */
moov = function(tracks, opt) {
  var
    i = tracks.length,
    boxes = [],
    duration = 0;
  opt = opt||{};
  while (i--) {
    boxes[i] = trak(tracks[i]);
    if (opt.set_duration) {
      duration = Math.max(duration,
        Math.floor(tracks[i].duration*90000/tracks[i].samplerate));
    }
  }
  duration = opt.duration||duration||0xFFFFFFFF;
  return box.apply(null, [types.moov, mvhd(duration)].concat(boxes)
    .concat(mvex(tracks)));
};
mvex = function(tracks) {
  var
    i = tracks.length,
    boxes = [];

  while (i--) {
    boxes[i] = trex(tracks[i]);
    if (tracks[i].cslg && tracks[i].cslg.max_cts) {
      boxes.push(trep(tracks[i]));
    }
  }
  return box.apply(null, [types.mvex].concat(boxes));
};
mvhd = function(duration) {
  var
    bytes = new Uint8Array([
      0x00, // version 0
      0x00, 0x00, 0x00, // flags
      0x00, 0x00, 0x00, 0x01, // creation_time
      0x00, 0x00, 0x00, 0x02, // modification_time
      0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
      (duration & 0xFF000000) >> 24,
      (duration & 0xFF0000) >> 16,
      (duration & 0xFF00) >> 8,
      duration & 0xFF, // duration
      0x00, 0x01, 0x00, 0x00, // 1.0 rate
      0x01, 0x00, // 1.0 volume
      0x00, 0x00, // reserved
      0x00, 0x00, 0x00, 0x00, // reserved
      0x00, 0x00, 0x00, 0x00, // reserved
      0x00, 0x01, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x01, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00,
      0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, // pre_defined
      0xff, 0xff, 0xff, 0xff // next_track_ID
    ]);
  return box(types.mvhd, bytes);
};

sdtp = function(track) {
  var
    samples = track.samples || [],
    bytes = new Uint8Array(4 + samples.length),
    flags,
    i;

  // leave the full box header (4 bytes) all zero

  // write the sample table
  for (i = 0; i < samples.length; i++) {
    flags = samples[i].flags;

    bytes[i + 4] = (flags.isLeading << 6) |
      (flags.dependsOn << 4) |
      (flags.isDependedOn << 2) |
      (flags.hasRedundancy);
  }

  return box(types.sdtp,
             bytes);
};

stbl = function(track) {
  return box(types.stbl,
             stsd(track),
             box(types.stts, STTS),
             box(types.stsc, STSC),
             box(types.stsz, STSZ),
             box(types.stco, STCO));
};

(function() {
  var videoSample, audioSample;

  stsd = function(track) {

    return box(types.stsd, new Uint8Array([
      0x00, // version 0
      0x00, 0x00, 0x00, // flags
      0x00, 0x00, 0x00, 0x01
    ]), track.type === 'video' ? videoSample(track) : audioSample(track));
  };

  videoSample = function(track) {
    var
      sps = track.sps || [],
      pps = track.pps || [],
      sequenceParameterSets = [],
      pictureParameterSets = [],
      i;

    // assemble the SPSs
    for (i = 0; i < sps.length; i++) {
      sequenceParameterSets.push((sps[i].byteLength & 0xFF00) >>> 8);
      sequenceParameterSets.push((sps[i].byteLength & 0xFF)); // sequenceParameterSetLength
      sequenceParameterSets = sequenceParameterSets.concat(Array.prototype.slice.call(sps[i])); // SPS
    }

    // assemble the PPSs
    for (i = 0; i < pps.length; i++) {
      pictureParameterSets.push((pps[i].byteLength & 0xFF00) >>> 8);
      pictureParameterSets.push((pps[i].byteLength & 0xFF));
      pictureParameterSets = pictureParameterSets.concat(Array.prototype.slice.call(pps[i]));
    }

    return box(types.avc1, new Uint8Array([
      0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, // reserved
      0x00, 0x01, // data_reference_index
      0x00, 0x00, // pre_defined
      0x00, 0x00, // reserved
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, // pre_defined
      (track.width & 0xff00) >> 8,
      track.width & 0xff, // width
      (track.height & 0xff00) >> 8,
      track.height & 0xff, // height
      0x00, 0x48, 0x00, 0x00, // horizresolution
      0x00, 0x48, 0x00, 0x00, // vertresolution
      0x00, 0x00, 0x00, 0x00, // reserved
      0x00, 0x01, // frame_count
      0x13,
      0x76, 0x69, 0x64, 0x65,
      0x6f, 0x6a, 0x73, 0x2d,
      0x63, 0x6f, 0x6e, 0x74,
      0x72, 0x69, 0x62, 0x2d,
      0x68, 0x6c, 0x73, 0x00,
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, // compressorname
      0x00, 0x18, // depth = 24
      0x11, 0x11 // pre_defined = -1
    ]), box(types.avcC, new Uint8Array([
      0x01, // configurationVersion
      track.profileIdc, // AVCProfileIndication
      track.profileCompatibility, // profile_compatibility
      track.levelIdc, // AVCLevelIndication
      0xff // lengthSizeMinusOne, hard-coded to 4 bytes
    ].concat([
      sps.length|0xE0 // reserved (high 3 bits) | numOfSequenceParameterSets
    ]).concat(sequenceParameterSets).concat([
      pps.length // numOfPictureParameterSets
    ]).concat(pictureParameterSets))), // "PPS"
            box(types.btrt, new Uint8Array([
              0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
              0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
              0x00, 0x2d, 0xc6, 0xc0
            ])) // avgBitrate
              );
  };

  audioSample = function(track) {
    return box(types.mp4a, new Uint8Array([

      // SampleEntry, ISO/IEC 14496-12
      0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, // reserved
      0x00, 0x01, // data_reference_index

      // AudioSampleEntry, ISO/IEC 14496-12
      0x00, 0x00, 0x00, 0x00, // reserved
      0x00, 0x00, 0x00, 0x00, // reserved
      (track.channelcount & 0xff00) >> 8,
      (track.channelcount & 0xff), // channelcount

      (track.samplesize & 0xff00) >> 8,
      (track.samplesize & 0xff), // samplesize
      0x00, 0x00, // pre_defined
      0x00, 0x00, // reserved

      (track.samplerate & 0xff00) >> 8,
      (track.samplerate & 0xff),
      0x00, 0x00 // samplerate, 16.16

      // MP4AudioSampleEntry, ISO/IEC 14496-14
    ]), esds(track));
  };
}());

styp = function() {
  return box(types.styp, MAJOR_BRAND, MINOR_VERSION, MAJOR_BRAND);
};

tkhd = function(track) {
  var duration = track.duration;
  if (track.samplerate) {
    // tkhd duration should be in movie scale
    duration = Math.floor(duration*90000/track.samplerate);
  }
  var result = new Uint8Array([
    0x00, // version 0
    0x00, 0x00, 0x07, // flags
    0x00, 0x00, 0x00, 0x00, // creation_time
    0x00, 0x00, 0x00, 0x00, // modification_time
    (track.id & 0xFF000000) >> 24,
    (track.id & 0xFF0000) >> 16,
    (track.id & 0xFF00) >> 8,
    track.id & 0xFF, // track_ID
    0x00, 0x00, 0x00, 0x00, // reserved
    (track.duration & 0xFF000000) >> 24,
    (track.duration & 0xFF0000) >> 16,
    (track.duration & 0xFF00) >> 8,
    track.duration & 0xFF, // duration
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, // reserved
    0x00, 0x00, // layer
    0x00, 0x00, // alternate_group
    +(track.type=='audio'), 0x00, // non-audio track volume
    0x00, 0x00, // reserved
    0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x01, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
    (track.width & 0xFF00) >> 8,
    track.width & 0xFF,
    0x00, 0x00, // width
    (track.height & 0xFF00) >> 8,
    track.height & 0xFF,
    0x00, 0x00 // height
  ]);

  return box(types.tkhd, result);
};

/**
 * Generate a track fragment (traf) box. A traf box collects metadata
 * about tracks in a movie fragment (moof) box.
 */
traf = function(track, opt) {
  var trackFragmentHeader, trackFragmentDecodeTime,
      trackFragmentRun, sampleDependencyTable, dataOffset,
      upperWordBaseMediaDecodeTime, lowerWordBaseMediaDecodeTime;
  opt = opt||{};
  trackFragmentHeader = box(types.tfhd, new Uint8Array([
    0x00, // version 0
    opt.no_multi_init ? 0x02 : 0x00, 0x00, 0x3a, // flags
    (track.id & 0xFF000000) >> 24,
    (track.id & 0xFF0000) >> 16,
    (track.id & 0xFF00) >> 8,
    (track.id & 0xFF), // track_ID
    0x00, 0x00, 0x00, 0x01, // sample_description_index
    0x00, 0x00, 0x00, 0x00, // default_sample_duration
    0x00, 0x00, 0x00, 0x00, // default_sample_size
    0x00, 0x00, 0x00, 0x00  // default_sample_flags
  ]));

  upperWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime / (UINT32_MAX + 1));
  lowerWordBaseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime % (UINT32_MAX + 1));
  trackFragmentDecodeTime = box(types.tfdt, new Uint8Array([
    0x01, // version 1
    0x00, 0x00, 0x00, // flags
    // baseMediaDecodeTime
    (upperWordBaseMediaDecodeTime >>> 24) & 0xFF,
    (upperWordBaseMediaDecodeTime >>> 16) & 0xFF,
    (upperWordBaseMediaDecodeTime >>>  8) & 0xFF,
    upperWordBaseMediaDecodeTime & 0xFF,
    (lowerWordBaseMediaDecodeTime >>> 24) & 0xFF,
    (lowerWordBaseMediaDecodeTime >>> 16) & 0xFF,
    (lowerWordBaseMediaDecodeTime >>>  8) & 0xFF,
    lowerWordBaseMediaDecodeTime & 0xFF
  ]));

  // the data offset specifies the number of bytes from the start of
  // the containing moof to the first payload byte of the associated
  // mdat
  dataOffset = (32 + // tfhd
                20 + // tfdt
                8 +  // traf header
                16 + // mfhd
                8 +  // moof header
                8);  // mdat header

  // audio tracks require less metadata
  if (track.type === 'audio') {
    trackFragmentRun = trun(track, dataOffset);
    return box(types.traf,
               trackFragmentHeader,
               trackFragmentDecodeTime,
               trackFragmentRun);
  }

  // video tracks should contain an independent and disposable samples
  // box (sdtp)
  // generate one and adjust offsets to match
  sampleDependencyTable = sdtp(track);
  trackFragmentRun = trun(track,
                          sampleDependencyTable.length + dataOffset);
  return box(types.traf,
             trackFragmentHeader,
             trackFragmentDecodeTime,
             trackFragmentRun,
             sampleDependencyTable);
};

/**
 * Generate a track box.
 * @param track {object} a track definition
 * @return {Uint8Array} the track box
 */
trak = function(track) {
  track.duration = track.duration || 0xffffffff;
  var param = [types.trak, tkhd(track), mdia(track)];
  if (track.edit_list && track.edit_list.length)
      param.splice(2, 0, edts(track));
  return box.apply(null, param);
};

trep = function(track){
  return box(types.trep, new Uint8Array([
    0x00, // version 0
    0x00, 0x00, 0x00, // flags
    (track.id & 0xFF000000) >> 24,
    (track.id & 0xFF0000) >> 16,
    (track.id & 0xFF00) >> 8,
    (track.id & 0xFF), // track_ID
  ]), cslg(track.cslg));
};

trex = function(track) {
  var result = new Uint8Array([
    0x00, // version 0
    0x00, 0x00, 0x00, // flags
    (track.id & 0xFF000000) >> 24,
    (track.id & 0xFF0000) >> 16,
    (track.id & 0xFF00) >> 8,
    (track.id & 0xFF), // track_ID
    0x00, 0x00, 0x00, 0x01, // default_sample_description_index
    0x00, 0x00, 0x00, 0x00, // default_sample_duration
    0x00, 0x00, 0x00, 0x00, // default_sample_size
    0x00, 0x01, 0x00, 0x01 // default_sample_flags
  ]);
  // the last two bytes of default_sample_flags is the sample
  // degradation priority, a hint about the importance of this sample
  // relative to others. Lower the degradation priority for all sample
  // types other than video.
  if (track.type !== 'video') {
    result[result.length - 1] = 0x00;
  }

  return box(types.trex, result);
};

trun = function(track, offset) {
  function get_flags(sample){
    return ('duration' in sample&&0x1)|('size' in sample&&0x2)|
        ('flags' in sample&&0x4)|('compositionTimeOffset' in sample&&0x8);
  }
  function trun_header(samples, offset, flags) {
    return [
      0x00, // version 0
      0x00,
      flags,
      0x01, // flags
      (samples.length & 0xFF000000) >>> 24,
      (samples.length & 0xFF0000) >>> 16,
      (samples.length & 0xFF00) >>> 8,
      samples.length & 0xFF, // sample_count
      (offset & 0xFF000000) >>> 24,
      (offset & 0xFF0000) >>> 16,
      (offset & 0xFF00) >>> 8,
      offset & 0xFF // data_offset
    ];
  }
  var samples = track.samples||[];
  var flags = get_flags(samples[0]||{});
  offset += 20+4*samples.length*((flags>>3&1)+(flags>>2&1)+(flags>>1&1)+
    (flags&1));
  var bytes = trun_header(samples, offset, flags);
  var was_neg = false;
  for (var i=0; i<samples.length; i++) {
    var sample = samples[i];
    if (flags&1){
      bytes.push(sample.duration>>24&0xFF, sample.duration>>16&0xFF,
        sample.duration>>8&0xFF, sample.duration&0xFF); // sample_duration
    }
    if (flags&2){
      bytes.push(sample.size>>24&0xFF, sample.size>>16&0xFF,
        sample.size>>8&0xFF, sample.size&0xFF); // sample_size
    }
    if (flags&4){
      bytes.push(sample.flags.isLeading<<2|sample.flags.dependsOn,
        sample.flags.isDependedOn<<6|sample.flags.hasRedundancy<<4|
        sample.flags.paddingValue<<1|sample.flags.isNonSyncSample,
        sample.flags.degradationPriority>>8&0xFF,
        sample.flags.degradationPriority&0xFF); // sample_flags
    }
    if (flags&8){
      was_neg = was_neg||sample.compositionTimeOffset<0;
      bytes.push(sample.compositionTimeOffset>>24&0xFF,
        sample.compositionTimeOffset>>16&0xFF,
        sample.compositionTimeOffset>>8&0xFF,
        sample.compositionTimeOffset&0xFF); // sample_composition_time_offset
    }
  }
  bytes[0] = +!!was_neg;
  return box(types.trun, new Uint8Array(bytes));
};

module.exports = {
  ftyp: ftyp,
  mdat: mdat,
  moof: moof,
  moov: moov,
  initSegment: function(tracks, opt) {
    var
      fileType = ftyp(opt),
      movie = moov(tracks, opt),
      result;

    result = new Uint8Array(fileType.byteLength + movie.byteLength);
    result.set(fileType);
    result.set(movie, fileType.byteLength);
    return result;
  }
};

},{}],36:[function(_dereq_,module,exports){
'use strict';

var Stream = _dereq_('../utils/stream.js');
var mp4 = _dereq_('./mp4-generator.js');
var sample_type={vide: 'video', soun: 'audio'};
var full_box = ['meta', 'mvhd', 'tkhd', 'mdhd', 'smhd', 'vmhd', 'dref',
    'hdlr', 'stsd', 'esds', 'stts', 'stps', 'stss', 'ctts', 'stsc', 'stsz',
    'stco', 'esds', 'elst', 'nmhd', 'cslg', 'sdtp', 'co64'];
// tgas, elng - non-standard unknown boxes seen in some videos
var raw_copy = ['udta', 'smhd', 'vmhd', 'dref', 'iods', 'btrt', 'pasp', 'clap',
    'uuid', 'colr', 'sbgp', 'sgpd', 'gmhd', 'tref', 'nmhd', 'svcC', 'hmhd',
    'fiel', 'tapt', 'load', 'meta', 'sefd', 'beam', 'tgas', 'elng'];
var containers = {
    trak: {name: 'track_info', multi: 1},
    edts: {name: 'edit_list'},
    exts: {name: 'edit_list'},
    mdia: {name: 'media_box'},
    minf: {name: 'media_info'},
    dinf: {name: 'data_info'},
    stbl: {name: 'sample_table'},
};
function byte_to_hex(bt){ return ('0'+bt.toString(16)).slice(-2); }
function int_to_str(tp){
    return String.fromCharCode(tp>>24&255, tp>>16&255, tp>>8&255, tp&255); }
function getUint64(view, ptr){
    return view.getUint32(ptr+4)+view.getUint32(ptr)*0x100000000; }
function getInt64(view, ptr){
    var hib = view.getUint8(ptr);
    if (hib<128)
        return getUint64(view, ptr);
    return view.getUint32(ptr+4)+0x100000000*(view.getUint32(ptr)-0x100000000);
}
function Bit_reader(view, ptr, size){
    var pos = 0, len = size*8;
    this.read = function(count, peek){
        var ret = 0, p = pos>>3, r = 7-pos%8;
        for (var i = 0, c = view.getUint8(p+ptr); i<count; i++, r--)
        {
            ret = (ret<<1)|((c>>r)&1);
            if (r)
                continue;
            p++;
            r = 8;
            c = view.getUint8(p+ptr);
        }
        if (!peek)
            pos += count;
        return ret;
    };
    this.bits = function(){ return len-pos; };
}
var Box_parser = function(){};
Box_parser.prototype = {};
Box_parser.prototype.header = function(opt){
    while (opt.ptr+opt.buffer.b_pos>=opt.branch.last)
    {
        if (opt.branch._id=='movie_box')
            opt.root.h_parsed = true;
        opt.branch = opt.branch.parent;
    }
    opt.type = null;
    opt.offset = 8;
    if (opt.buffer.b_size-opt.ptr<8)
        return (opt.offset = 0);
    opt.size = opt.view.getUint32(opt.ptr);
    if (opt.size==1)
    {
        opt.offset = 16;
        if (opt.buffer.b_size-opt.ptr<16)
            return (opt.offset = 0);
        opt.size = (opt.view.getUint32(opt.ptr+8)<<32)+
            opt.view.getUint32(opt.ptr+12);
    }
    opt.type = int_to_str(opt.view.getUint32(opt.ptr+4));
    if (full_box.includes(opt.type))
    {
        opt.offset += 4;
        if (opt.buffer.b_size-opt.ptr<opt.offset)
            return (opt.offset = 0);
        var extra = opt.view.getUint8(opt.ptr+opt.offset-4);
        opt.ver = extra>>>24;
        opt.flags = extra&&0xFFFFFF;
    }
    opt.size -= opt.offset;
    opt.ptr += opt.offset;
};
Box_parser.prototype.parse = function(opt){
    if (!this[opt.type])
        throw new Error('Unknown box type: '+opt.type);
    this[opt.type](opt);
};
raw_copy.forEach(function(cont){
    Box_parser.prototype[cont] = function(opt){
        var data = opt.branch[cont] = new Uint8Array(opt.size);
        data.set(opt.buffer._buff.subarray(opt.ptr, opt.ptr+opt.size));
    };
});
Object.keys(containers).forEach(function(cont){
    Box_parser.prototype[cont] = function(opt){
        var elm = containers[cont];
        opt.branch[elm.name] = opt.branch[elm.name]||(elm.multi ? [] : {});
        var new_branch = opt.branch[elm.name];
        if (elm.multi)
        {
            new_branch.push({});
            new_branch = new_branch[new_branch.length-1];
        }
        new_branch.parent = opt.branch;
        new_branch.last = opt.buffer.b_pos+opt.ptr+opt.size;
        new_branch._id = elm.name;
        opt.branch = new_branch;
        opt.size = 0;
    };
});
Box_parser.prototype.moov = function(opt){
    var new_branch = opt.branch.movie_box = opt.branch.movie_box||{};
    new_branch.parent = opt.branch;
    new_branch.last = opt.buffer.b_pos+opt.ptr+opt.size;
    new_branch._id = 'movie_box';
    // only ftyp can exist prior to moov in a file beginning
    if (opt.buffer.b_pos<256)
        opt.branch.start_hdr_sz = opt.size;
    else
        opt.branch.end_hdr_sz = opt.size;
    opt.branch = new_branch;
    opt.size = 0;
};
function get_hd_times(opt){
    var view = opt.view, ptr = opt.ptr, is_tk = opt.type=='tkhd';
    if (!opt.ver)
    {
        return [view.getUint32(ptr), view.getUint32(ptr+4),
            view.getUint32(ptr+8), view.getUint32(ptr+(is_tk ? 16 : 12))];
    }
    return [getUint64(view, ptr), getUint64(view, ptr+8),
        view.getUint32(ptr+16), getUint64(view, ptr+(is_tk ? 24 : 20))];

}
function get_table(view, ptr, cnt, tbl){
    for (var i=0; i<cnt; i++)
        tbl[i] = view.getUint32(ptr+i*4);
}
Box_parser.prototype.ftyp = function(opt){
    opt.branch.major_brand = int_to_str(opt.view.getUint32(opt.ptr));
    opt.branch.minor_version = opt.view.getUint32(opt.ptr+4);
    opt.branch.compatible = [opt.branch.major_brand];
    for (var i=8; i<opt.size; i+=4)
        opt.branch.compatible.push(int_to_str(opt.view.getUint32(opt.ptr+i)));
};
Box_parser.prototype.mdat = function(){};
Box_parser.prototype.free = function(){};
Box_parser.prototype.wide = function(){};
Box_parser.prototype.skip = function(){};
Box_parser.prototype.mvhd = function(opt){
    var view = opt.view, ptr = opt.ptr;
    var offset = opt.ver ? 28 : 16;
    var hdr = opt.branch.mv_hdr = {};
    var times = get_hd_times(opt);
    hdr.creation_time = times[0];
    hdr.modification_time = times[1];
    hdr.time_scale = times[2];
    hdr.duration = times[3];
    ptr += offset;
    hdr.rate = view.getUint32(ptr)/65536.0;
    hdr.volume = view.getUint16(ptr+4)/256.0;
    get_table(opt.view, ptr+16, 9, hdr.matrix = []);
    hdr.next_track = view.getUint32(ptr+76);
};
Box_parser.prototype.tkhd = function(opt){
    var view = opt.view, ptr = opt.ptr;
    var offset = opt.ver ? 40 : 28;
    var hdr = opt.branch.tk_hdr = {};
    var times = get_hd_times(opt);
    hdr.enabled = !!(opt.flags&1);
    hdr.in_movie = !!(opt.flags&2);
    hdr.in_preview = !!(opt.flags&4);
    hdr.creation_time = times[0];
    hdr.modification_time = times[1];
    hdr.track_id = times[2];
    hdr.duration = times[3];
    ptr += offset;
    hdr.layer = view.getUint16(ptr);
    hdr.alternate_group = view.getUint16(ptr+2);
    hdr.volume = view.getInt16(ptr+4)/256.0;
    get_table(opt.view, ptr+8, 9, hdr.matrix = []);
    hdr.width = view.getUint32(ptr+44)/65536.0;
    hdr.height = view.getUint32(ptr+48)/65536.0;
};
Box_parser.prototype.mdhd = function(opt){
    var view = opt.view, ptr = opt.ptr;
    var offset = opt.ver ? 28 : 16;
    var hdr = opt.branch.md_hdr = {};
    var times = get_hd_times(opt);
    hdr.creation_time = times[0];
    hdr.modification_time = times[1];
    hdr.time_scale = times[2];
    hdr.duration = times[3];
    var lang = view.getUint16(ptr+offset);
    hdr.lang = String.fromCharCode(lang>>10|96, lang>>5&31|96, lang&31|96);
};
Box_parser.prototype.elst = function(opt){
    var view = opt.view, ptr = opt.ptr+4;
    var count = view.getUint32(opt.ptr);
    opt.branch.list = [];
    for (var i=0; i<count; ptr += 4, i++)
    {
        var elm = {};
        elm.segment_duration = opt.ver ?
            getUint64(view, ptr) : view.getUint32(ptr);
        elm.media_time = opt.ver ?
            getInt64(view, ptr+8) : view.getInt32(ptr+4);
        ptr += opt.ver ? 16 : 8;
        elm.media_rate = view.getInt16(ptr);
        opt.branch.list.push(elm);
    }
};
Box_parser.prototype.cslg = function(opt){
    var view = opt.view, ptr = opt.ptr;
    opt.branch.cslg = {
        ctts_shift: view.getInt32(ptr),
        min_ctts: view.getInt32(ptr+4),
        max_ctts: view.getInt32(ptr+8),
        min_cts: view.getInt32(ptr+12),
        max_cts: view.getInt32(ptr+16),
    };
};
Box_parser.prototype.hdlr = function(opt){
    opt.branch.handler = int_to_str(opt.view.getUint32(opt.ptr+4)); };
Box_parser.prototype._parse_avcc = function(opt, elm){
    var view = opt.view, ptr = opt.ptr;
    var sps = elm.sps = [];
    var pps = elm.pps = [];
    elm.avc_p_i = view.getUint8(ptr+1);
    elm.prof_compat = view.getUint8(ptr+2);
    elm.avc_l_i = view.getUint8(ptr+3);
    elm.l_size_m_1 = view.getUint8(ptr+4)&3;
    elm.n_sps = view.getUint8(ptr+5)&31;
    var offset = 6;
    for (var i=0; i<elm.n_sps; i++)
    {
        sps[i] = {l: view.getUint16(ptr+offset)};
        offset += 2;
        sps[i].nal = new Uint8Array(opt.buffer._buff.subarray(ptr+offset,
            ptr+offset+sps[i].l));
        offset += sps[i].l;
    }
    elm.n_pps = view.getUint8(ptr+offset++);
    for (i=0; i<elm.n_pps; i++)
    {
        pps[i] = {l: view.getUint16(ptr+offset)};
        offset += 2;
        pps[i].nal = new Uint8Array(opt.buffer._buff.subarray(ptr+offset,
            ptr+offset+pps[i].l));
        offset += pps[i].l;
    }
};
Box_parser.prototype._parse_esds = function(opt, elm){
    var view = opt.view, ptr = opt.ptr;
    while (ptr<opt.ptr+opt.size)
    {
        var tag = view.getUint8(ptr++);
        var sz = 0, sb;
        do {
            sb = view.getUint8(ptr++);
            sz = (sz<<7)+(sb&0x7F);
        } while (sb&0x80);
        switch (tag)
        {
        case 3: // ES_DescrTag
            elm.es_id = view.getUint16(ptr);
            var flags = view.getUint8(ptr+2);
            ptr += 3+(flags>>6&2)+(flags>>4&2);
            break;
        case 4: // DecoderConfigDescrTag
            elm.obj_t = view.getUint8(ptr);
            elm.str_t = view.getUint8(ptr+1)&0x3F;
            ptr += 13;
            break;
        case 5: // DecoderSpecificInfoTag
            var ext_type, br = new Bit_reader(view, ptr, sz);
            elm.aot = br.read(5);
            if (elm.aot==31)
                elm.aot = 32+br.read(6);
            elm.freq = br.read(4);
            if (elm.freq==15)
                elm.freq = br.read(24);
            elm.channel = br.read(4);
            // Read ext configuration for explicitly signaled HE-AAC profiles
            // 5 = HEv1, 29 = HEv2
            if (elm.aot==5 || elm.aot==29)
            {
                ext_type = 5;
                if ((elm.ext_freq_index = br.read(4))==0xf)
                    elm.ext_freq = br.read(24);
                // With HE extensions now known, determine underlying profile.
                elm.aot = br.read(5);
                if (elm.aot==31)
                    elm.aot = 32+br.read(6);
            }
            if (ext_type!=5 && elm.aot!=36)
            {
                while (br.bits()>=16)
                {
                    if (br.read(11, 1)==0x2b7) // sync_ext_type
                    {
                        br.read(11);
                        if (br.read(5)==5) // type_ext
                        {
                            if (br.read(1)) // sbr
                            {
                                if (br.read(4)==0xf) // sr_ext
                                    br.read(24);
                                if (br.bits()>=12 && br.read(11)==0x548 &&
                                    br.read(1)!=1)
                                {
                                    elm.dsi = 5;
                                }
                            }
                        }
                    }
                    else
                        br.read(1);
                }
            }
            ptr += sz;
            break;
        default:
            ptr += sz;
        }
    }
};
Box_parser.prototype.stsd = function(opt){
    var view = opt.view, count = view.getUint32(opt.ptr);
    var handler = opt.branch.parent.parent.handler, i, j;
    opt.branch.list = {};
    opt.ptr += 4;
    for (i=0; i<count; i++)
    {
        this.header(opt);
        var elm = {};
        var index = view.getUint16(opt.ptr+6);
        var last_ptr = opt.ptr+opt.size;
        switch (handler)
        {
        case 'vide':
            elm.width = view.getUint16(opt.ptr+24);
            elm.height = view.getUint16(opt.ptr+26);
            elm.h_res = view.getUint32(opt.ptr+28)/65536.0;
            elm.v_res = view.getUint32(opt.ptr+32)/65536.0;
            elm.f_count = view.getUint16(opt.ptr+40);
            elm.compressor = '';
            for (j=0; j<32; j++)
            {
                var c = view.getUint8(opt.ptr+42+j);
                if (!c)
                    break;
                elm.compressor += String.fromCharCode();
            }
            elm.depth = view.getUint16(opt.ptr+74);
            // 'colr', 'clap', 'pasp' & 'fiel'
            var skip_boxes = [0x636F6C72, 0x636C6170, 0x70617370, 0x6669656C];
            while (skip_boxes.includes(view.getUint32(opt.ptr+82)))
                opt.ptr += view.getUint32(opt.ptr+78);
            if (view.getUint32(opt.ptr+82)==0x61766343) // avcC
            {
                elm.avcc = {};
                opt.ptr += 78;
                this.header(opt);
                this._parse_avcc(opt, elm.avcc);
            }
            // XXX pavelki: optional boxes
            break;
        case 'soun':
            elm.c_count = view.getUint16(opt.ptr+16);
            elm.s_size = view.getUint16(opt.ptr+18);
            elm.s_rate = view.getUint32(opt.ptr+24)/65536.0;
            for (j=32; j<opt.size-12; j++)
            {
                if (view.getUint32(opt.ptr+j)==0x65736473) // esds
                {
                    elm.esds = {};
                    opt.ptr += j-4;
                    this.header(opt);
                    this._parse_esds(opt, elm.esds);
                    break;
                }
            }
            break;
        default:
        }
        opt.ptr = last_ptr;
        opt.branch.list[index] = elm;
    }
    opt.size = 0;
};
Box_parser.prototype.stts = function(opt){
    var cnt = opt.view.getUint32(opt.ptr), ptr = opt.ptr+4;
    opt.branch.dtts = [];
    for (var i=0; i<cnt; i++)
    {
        var u_cnt = opt.view.getUint32(opt.ptr+4+i*8);
        var data = opt.view.getUint32(ptr+i*8+4);
        for (var j=0; j<u_cnt; j++)
             opt.branch.dtts.push(data);
    }
};
Box_parser.prototype.ctts = function(opt){
    var cnt = opt.view.getUint32(opt.ptr), ptr = opt.ptr+4;
    opt.branch.ctts = [];
    for (var i=0; i<cnt; i++)
    {
        var u_cnt = opt.view.getUint32(ptr+i*8);
        var data = opt.view.getInt32(ptr+i*8+4);
        for (var j=0; j<u_cnt; j++)
            opt.branch.ctts.push(data);
    }
};
Box_parser.prototype.stsc = function(opt){
    var count = opt.view.getUint32(opt.ptr);
    var table = opt.branch.s_t_c = [];
    var view = opt.view, ptr = opt.ptr+4;
    for (var i=0; i<count; i++)
    {
        table[i] = {
            f_c: view.getUint32(ptr+i*12),
            s_p_c: view.getUint32(ptr+i*12+4),
            s_d_i: view.getUint32(ptr+i*12+8),
        };
    }
    table.sort(function(c1, c2){ return c1.f_c-c2.f_c; });
};
Box_parser.prototype.stss = function(opt){
    get_table(opt.view, opt.ptr+4, opt.view.getUint32(opt.ptr),
        opt.branch.s_sync = []);
};
Box_parser.prototype.stps = function(opt){
    get_table(opt.view, opt.ptr+4, opt.view.getUint32(opt.ptr),
        opt.branch.s_psync = []);
};
Box_parser.prototype.sdtp = function(opt){
    opt.branch.s_dep = [];
    for (var i=0; i<opt.size; i++)
    {
        var bt = opt.view.getUint8(opt.ptr+i);
        opt.branch.s_dep[i] = {
            red: bt&3,
            is_dep: bt>>2&3,
            dep: bt>>4&3,
            lead: bt>>6&3,
        };
    }
};
Box_parser.prototype.stsz = function(opt){
    var size = opt.view.getUint32(opt.ptr);
    opt.branch.s_sz = [];
    opt.branch.s_count = opt.view.getUint32(opt.ptr+4);
    if (!size)
        get_table(opt.view, opt.ptr+8, opt.branch.s_count, opt.branch.s_sz);
    else
    {
        for (var i=0; i<opt.branch.s_count; i++)
            opt.branch.s_sz.push(size);
    }
};
Box_parser.prototype.stco = function(opt){
    get_table(opt.view, opt.ptr+4, opt.view.getUint32(opt.ptr),
        opt.branch.c_off = []);
};
Box_parser.prototype.co64 = function(opt){
    var cnt = opt.view.getUint32(opt.ptr);
    opt.branch.c_off = [];
    for (var i=0; i<cnt; i++)
        opt.branch.c_off[i] = getUint64(opt.view, opt.ptr+4+i*8);
};

var Chunk_parser = function(opt){
    this.conf_update(opt);
};
Chunk_parser.prototype = {};
Chunk_parser.prototype.conf_update = function(conf){
    this.break_on_count = conf.break_on_count;
    this.frag_size = conf.frag_size||10;
};
Chunk_parser.prototype.process = function(opt){
    var event = {
        type: 'metadata',
        tracks: [],
        brands: ['iso5', 'iso6'],
        matrix: opt.root.movie_box.mv_hdr.matrix,
        start_hdr_sz: opt.root.start_hdr_sz,
        end_hdr_sz: opt.root.end_hdr_sz,
    };
    var _this = this;
    event.duration = Math.floor(opt.root.movie_box.mv_hdr.duration*90000/
        opt.root.movie_box.mv_hdr.time_scale);
    event.timescale = 90000;
    event.s_info = this.s_info = [];
    event.s_p = this.s_p = [];
    opt.root.movie_box.track_info.forEach(function(tr){
        if (!['vide', 'soun'].includes(tr.media_box.handler))
            return;
        var elm = {
            id: tr.tk_hdr.track_id,
            ts: tr.media_box.md_hdr.time_scale,
            type: tr.media_box.handler,
            s_off: [],
            s_time: [],
            s_dri: [],
            s_sync: tr.media_box.media_info.sample_table.s_sync||[],
            s_psync: tr.media_box.media_info.sample_table.s_psync||[],
            s_sz: tr.media_box.media_info.sample_table.s_sz,
            s_dep: tr.media_box.media_info.sample_table.s_dep||[],
            s_ctts: tr.media_box.media_info.sample_table.ctts||[],
            s_cslg: tr.media_box.media_info.sample_table.cslg||{},
            s_list: tr.media_box.media_info.sample_table.list,
        };
        if (tr.edit_list && tr.edit_list.list.length)
            elm.elst = tr.edit_list.list;
        var c_off = tr.media_box.media_info.sample_table.c_off;
        var s_t_c = tr.media_box.media_info.sample_table.s_t_c;
        var dtts = tr.media_box.media_info.sample_table.dtts;
        var c_n = 1;
        var sn = 0;
        var dt = 0;
        var is_sz_arr = Array.isArray(elm.s_sz);
        for (var i = 0; i<s_t_c.length; i++)
        {
            for (; c_n<(s_t_c[i+1] ? s_t_c[i+1].f_c : c_off.length+1); c_n++)
            {
                var off = c_off[c_n-1];
                for (var j=0; j<s_t_c[i].s_p_c; j++)
                {
                    elm.s_off[sn] = off;
                    elm.s_time[sn] = dt;
                    elm.s_dri[sn] = s_t_c[i].s_d_i;
                    dt += dtts[sn];
                    off += is_sz_arr ? elm.s_sz[sn++] : elm.s_sz;
                }
            }
        }
        _this.s_info.push(elm);
        _this.s_p.push({s: 0, max_t: 0});
        if (elm.type=='vide')
            event.v_idx = _this.v_idx = _this.s_info.length-1;
        var dr = elm.s_list[1];
        var event_elm = {
            id: elm.id,
            type: sample_type[elm.type],
            edit_list: elm.elst,
            cslg: elm.s_cslg,
            dr: dr,
            timelineStartInfo: {baseMediaDecodeTime: 0},
            bitrate: Math.floor(elm.s_sz.reduce(function(a, b){ return a+b; })*
                8*elm.ts/tr.media_box.md_hdr.duration),
            duration: elm.type=='soun' ?
                tr.media_box.md_hdr.duration :
                Math.floor(tr.media_box.md_hdr.duration*90000/elm.ts),
            samplerate: elm.type=='soun' ? elm.ts : 90000,
            matrix: tr.tk_hdr.matrix,
            track_width: tr.tk_hdr.width,
            track_height: tr.tk_hdr.height,
            nb_samples: elm.s_time.length,
        };
        if (elm.type=='soun')
        {
            var aot = dr.esds.dsi||dr.esds.aot;
            event_elm.codec = 'mp4a.'+byte_to_hex(dr.esds.obj_t.toString(16))+
                (aot ? '.'+aot : '');
            event_elm.s_i = new Uint8Array([dr.esds.aot<<3|dr.esds.freq>>>1,
                dr.esds.freq<<7|dr.esds.channel<<3]);
        }
        else
        {
            event_elm.codec = 'avc1.'+byte_to_hex(dr.avcc.avc_p_i)+
                byte_to_hex(dr.avcc.prof_compat)+byte_to_hex(dr.avcc.avc_l_i);
            var info = [1, dr.avcc.avc_p_i, dr.avcc.avc_prof_compat,
                dr.avcc.avc_l_i, 255, dr.avcc.sps.length+224];
            dr.avcc.sps.forEach(function(e){
                info = info.concat([e.nal.length>>8, e.nal.length&255]);
                Array.prototype.push.apply(info, e.nal);
            });
            info.push(dr.avcc.pps.length);
            dr.avcc.pps.forEach(function(e){
                info = info.concat([e.nal.length>>8, e.nal.length&255]);
                Array.prototype.push.apply(info, e.nal);
            });
            event_elm.s_i = new Uint8Array(info);
        }
        if (event_elm.edit_list)
        {
            event_elm.edit_list.forEach(function(e){
                if (elm.type!='soun')
                    e.media_time = Math.floor(e.media_time*90000/elm.ts);
                e.segment_duration = Math.floor(e.segment_duration*90000/
                    opt.root.movie_box.mv_hdr.time_scale);
            });
        }
        if (event_elm.cslg)
        {
            for (var k in event_elm.cslg)
                event_elm.cslg[k] = Math.floor(event_elm.cslg[k]*90000/elm.ts);
        }
        event.tracks.push(event_elm);
    });
    opt.stream.trigger('data', event);
};
Chunk_parser.prototype.parse = function(opt){
    if (!this.s_info)
        this.process(opt);
    var b_start = opt.buffer.b_pos;
    var b_end = opt.buffer.b_size+b_start;
    var pc = -1, max_dcd = 0, i;
    var v_fin = false;
    while (pc)
    {
        pc = 0;
        for (i=0; i<this.s_p.length; i++)
        {
            var sinfo = this.s_info[i];
            var sn = this.s_p[i].s;
            var pos = sinfo.s_off[sn];
            var sz = sinfo.s_sz[sn];
            var time = sinfo.s_time;
            if (pos>=b_start && pos+sz<=b_end)
            {
                this.s_p[i].s++;
                pc++;
                var sample = {trackId: sinfo.id};
                sample.type = sample_type[sinfo.type];
                sample.dts = time[sn];
                sample.pts = sample.dts+(sinfo.s_ctts[sn]||0);
                sample.duration = sn==time.length-1 ?
                    time[sn]-time[sn-1] : time[sn+1]-time[sn];
                sample.size = sz;
                this.s_p[i].max_t = sample.dts/sinfo.ts;
                sample.data = opt.buffer._buff.subarray(pos-b_start,
                    pos+sz-b_start);
                sample.dr = sinfo.s_list[sinfo.s_dri[sn]];
                sample.ts = sinfo.ts;
                sample.synced =  !sinfo.s_sync.length ||
                    sinfo.s_sync.includes(sn+1);
                sample.sn = sn;
                sample.dep = sinfo.s_dep[sn];
                if (sinfo.type=='vide' && (this.break_on_count ?
                    sn%this.frag_size===0 : sn&&sample.synced))
                {
                    opt.stream.flush();
                }
                opt.stream.trigger('data', sample);
                max_dcd = pos+sz-b_start;
            }
            else if (pos+sz>b_end&&i==this.v_idx)
                v_fin = true;
        }
    }
    if (max_dcd)
        opt.buffer.advance(max_dcd);
    var new_pos = Infinity;
    for (i=0; i<this.s_p.length; i++)
    {
        if (this.s_p[i].s==this.s_info[i].s_off.length)
        {
            this.s_p[i].finish = true;
            continue;
        }
        new_pos = Math.min(new_pos, this.s_info[i].s_off[this.s_p[i].s]);
    }
    if (new_pos==Infinity)
        opt.stream.flush();
    if (new_pos==Infinity || new_pos>=b_start&&new_pos<b_end)
        new_pos = b_end;
    return new_pos;
};
Chunk_parser.prototype.seek = function(time, use_ss){
    if (!this.s_info)
        throw new Error('No metadata information for seeking');
    var target = time*90000;
    var m_pos = Infinity;
    function get_frame(target, elm, use_ss){
        var l, r, sn, m, m_time, tt;
        var i_ss = use_ss&&elm.s_sync.length>0;
        var i_soun = elm.type=='soun';
        var scale = elm.ts/90000;
        l = 0;
        r = i_ss ? elm.s_sync.length : elm.s_time.length;
        tt = Math.floor(target*scale);
        while (l<r-1)
        {
            m = (l+r)>>1;
            sn = i_ss ? elm.s_sync[m]-1 : m;
            if ((m_time = elm.s_time[sn]+(elm.s_ctts[sn]|0))>tt)
                r = m;
            else
            {
                l = m;
                if (m_time==tt)
                    break;
            }
        }
        var res_sn = i_ss ? elm.s_sync[l]-1 : l;
        tt = m_time = elm.s_time[res_sn]+(elm.s_ctts[res_sn]|0);
        if (!i_ss&&elm.s_ctts.length)
        {
            for (sn=l-1; sn>l-10; sn--)
            {
                if (elm.s_time[sn]+(elm.s_ctts[sn]|0)>tt)
                    res_sn = sn;
            }
            for (sn=res_sn; sn<l+10; sn++)
            {
                m_time = elm.s_time[sn]+(elm.s_ctts[sn]|0);
                if (m_time > ((elm.s_cslg&&elm.s_cslg.min_ctts)|0) &&
                    m_time<tt)
                {
                    tt = m_time;
                }
            }
        }
        return {sn: res_sn, min_t: tt/elm.ts};
    }
    if (this.v_idx!==undefined)
    {
        var elm = this.s_info[this.v_idx];
        var v_fr = get_frame(target, elm, use_ss);
        var v_sn = v_fr.sn;
        this.s_p[this.v_idx].s = v_sn;
        this.s_p[this.v_idx].max_t =
            (elm.s_time[v_sn]+(elm.s_ctts[v_sn]|0))/elm.ts;
        // sync audio track(s) to video
        target = v_fr.min_t*90000;
        m_pos = elm.s_off[v_sn];
    }
    var _this = this;
    this.s_info.forEach(function(elm, idx){
        if (idx==_this.v_idx)
            return;
        var s_sn = get_frame(target, elm, use_ss).sn;
        _this.s_p[idx].s = s_sn;
        _this.s_p[idx].max_t = (elm.s_time[s_sn]+(elm.s_ctts[s_sn]|0))/elm.ts;
        m_pos = Math.min(elm.s_off[s_sn], m_pos);
    });
    return {
        offset: m_pos,
        time: this.s_p[this.v_idx!==undefined ? this.v_idx : 0].max_t,
    };
};

var Buffer = function(){
    this._buff = new Uint8Array(3*1048576);
    this.b_pos = 0;
    this.b_size = 0;
    this.pos = 0;
};
Buffer.prototype = {};
Buffer.prototype.advance = function(ptr){
    this._buff.set(this._buff.subarray(ptr, this.b_size));
    this.b_pos += ptr;
    this.b_size -= ptr;
};
Buffer.prototype.push = function(chunk){
    var _newbuff;
    var c_len = chunk.length;
    while (c_len+this.b_size>this._buff.length)
    {
        _newbuff = new Uint8Array(2*this._buff.length);
        _newbuff.set(this._buff);
        this._buff = _newbuff;
    }
    if (this.pos<this.b_pos+this.b_size && this.pos+c_len>=this.b_pos)
    {
        _newbuff = new Uint8Array(Math.max(this.pos+c_len,
            this.b_pos+this.b_size)-Math.min(this.pos, this.b_pos));
        if (this.pos<=this.b_pos)
        {
            _newbuff.set(chunk);
            if (this.pos+c_len<this.b_pos+this.b_size)
            {
                _newbuff.set(this._buff.subarray(this.pos+c_len-this.b_pos,
                    this.b_size), c_len);
            }
        }
        else
        {
            _newbuff.set(this._buff.subarray(0, this.pos-this.b_pos));
            _newbuff.set(chunk, this.pos-this.b_pos);
        }
        this._buff.set(_newbuff);
        this.b_size = _newbuff.length;
        _newbuff = null;
        this.b_pos = Math.min(this.b_pos, this.pos);
    }
    else if (this.pos==this.b_pos+this.b_size)
    {
        this._buff.set(chunk, this.b_size);
        this.b_size += c_len;
    }
    else
    {
        this._buff.set(chunk);
        this.b_size = c_len;
        this.b_pos = this.pos;
    }
    this.view = new DataView(this._buff.buffer, this._buff.byteOffset,
        this.b_size);
};

var MP4ParserStream = function(opt){
    if (!(this instanceof MP4ParserStream))
        return new MP4ParserStream(opt);
    MP4ParserStream.prototype.init.call(this);
    this.buffer = new Buffer();
    this.b_parser = new Box_parser();
    this.c_parser = new Chunk_parser(opt);
    this.on('confupdate', function(conf){
        this.c_parser.conf_update(conf);
    });
    this.metadata = {};
    this.buffer.pos = opt.pos||0;
    if (opt.metadata)
    {
        this.metadata.h_parsed = true;
        this.c_parser.s_info = opt.metadata.s_info.slice();
        this.c_parser.s_p = opt.metadata.s_p.slice();
        this.c_parser.v_idx = opt.metadata.v_idx;
    }
};
MP4ParserStream.prototype = new Stream();
MP4ParserStream.prototype.constructor = MP4ParserStream;
MP4ParserStream.prototype.get_tl = function(id){
    if (!this.metadata.h_parsed)
        throw new Error('No metadata information for time map');
    var t_i = this.c_parser.s_info.filter(function(e){ return e.id==id; });
    if (!t_i.length)
        throw new Error('No track information for time map');
    return {
        offset: t_i[0].s_off.slice(0),
        time: t_i[0].s_time.map(function(e){ return e/t_i[0].ts; }),
    };
};
MP4ParserStream.prototype.push = function(chunk){
    this.buffer.push(chunk);
    var opt = {
        root: this.metadata,
        branch: this.metadata,
        buffer: this.buffer,
        view: this.buffer.view,
        stream: this,
        ptr: 0,
    };
    while (!this.metadata.h_parsed)
    {
        this.b_parser.header(opt);
        if (!opt.type || opt.size+opt.ptr>this.buffer.b_size)
            break;
        this.b_parser.parse(opt);
        opt.ptr += opt.size;
    }
    if (this.metadata.h_parsed)
        this.buffer.pos = this.c_parser.parse(opt);
    else
    {
        this.buffer.advance(opt.ptr-opt.offset);
        this.buffer.pos = opt.type=='mdat' ?
            opt.ptr+opt.size : this.buffer.b_pos+this.buffer.b_size;
    }
    return this.buffer.pos;
};
MP4ParserStream.prototype.seek = function(time, use_ssync){
    this.trigger('data', {type: 'seek'});
    var seek_info = this.c_parser.seek(time, use_ssync);
    this.buffer.pos = seek_info.offset;
    return seek_info;
};

var AudioFilterStream = function(){
    if (!(this instanceof AudioFilterStream))
        return new AudioFilterStream();
    AudioFilterStream.prototype.init.call(this);
};
AudioFilterStream.prototype = new Stream();
AudioFilterStream.prototype.constructor = AudioFilterStream;
AudioFilterStream.prototype.push = function(packet){
    if (packet.type!='audio')
        return;
    var scale = 90000/packet.ts;
    packet.pts = Math.floor(packet.pts*scale);
    packet.dts = Math.floor(packet.dts*scale);
    this.trigger('data', {
        type: 'audio',
        samplerate: packet.dr.s_rate,
        samplesize: packet.dr.s_size,
        audioobjecttype: packet.dr.esds.aot,
        samplingfrequencyindex: packet.dr.esds.freq,
        channelcount: packet.dr.esds.channel,
        ts: packet.ts,
        dts: packet.dts,
        pts: packet.pts,
        data: new Uint8Array(packet.data),
    });
};

var VideoFilterStream = function(){
    if (!(this instanceof VideoFilterStream))
        return new VideoFilterStream();
    VideoFilterStream.prototype.init.call(this);
    this.synced = false;
    this.au = new Uint8Array([0x09, 0xF0]);
};
VideoFilterStream.prototype = new Stream();
VideoFilterStream.prototype.constructor = VideoFilterStream;
VideoFilterStream.prototype.flush = function(){
    this.dr = null;
    this.synced = false;
    this.trigger('done');
};
VideoFilterStream.prototype.push = function(packet){
    if (packet.type!='video')
        return;
    var pos = 0, i;
    var view = new DataView(packet.data.buffer, packet.data.byteOffset,
        packet.data.byteLength);
    var scale = 90000/packet.ts;
    packet.pts = Math.floor(packet.pts*scale);
    packet.dts = Math.floor(packet.dts*scale);
    this.trigger('data', {
        trackId: packet.trackId,
        pts: packet.pts,
        dts: packet.dts,
        data: this.au,
        nalUnitType: 'access_unit_delimiter_rbsp',
    });
    if (this.dr!=packet.dr||!this.synced)
    {
        this.dr = packet.dr;
        // pseudo-nals for config info
        if (this.dr.avcc.n_sps)
        {
            for (i=0; i<this.dr.avcc.n_sps; i++)
            {
                this.trigger('data', {
                    trackId: packet.trackId,
                    pts: packet.pts,
                    dts: packet.dts,
                    nalUnitType: 'seq_parameter_set_rbsp',
                    data: this.dr.avcc.sps[i].nal,
                    config: {
                        profileIdc: this.dr.avcc.avc_p_i,
                        levelIdc: this.dr.avcc.avc_l_i,
                        profileCompatibility: this.dr.avcc.prof_compat,
                        width: this.dr.width,
                        height: this.dr.height,
                    },
                });
            }
        }
        if (this.dr.avcc.n_pps)
        {
            for (i=0; i<this.dr.avcc.n_pps; i++)
            {
                this.trigger('data', {
                    trackId: packet.trackId,
                    pts: packet.pts,
                    dts: packet.dts,
                    nalUnitType: 'pic_parameter_set_rbsp',
                    data: this.dr.avcc.pps[0].nal,
                });
            }
        }
        this.synced = true;
    }
    while (pos<packet.data.length)
    {
        var sz = view.getUint32(pos);
        var event = {
            trackId: packet.trackId,
            pts: packet.pts,
            dts: packet.dts,
            data: new Uint8Array(packet.data.subarray(pos+4, pos+sz+4)),
        };
        if ((event.data[0]&0x1f)==5)
            event.nalUnitType = 'slice_layer_without_partitioning_rbsp_idr';
        event.synced = !pos && packet.synced;
        pos += sz+4;
        this.trigger('data', event);
    }
};

var MP4BuilderStream = function(opt){
    if (!(this instanceof MP4BuilderStream))
        return new MP4BuilderStream(opt);
    MP4BuilderStream.prototype.init.call(this);
    this.options = opt||{};
    this.tracks = {};
    this.options.no_multi_init = true;
    this.options.major = new Uint8Array([105, 115, 111, 53]); // 'iso5'
    this.options.compatible = [new Uint8Array([105, 115, 111, 54])]; // 'iso6'
    this.options.set_duration = true;
    this.on('confupdate', function(conf){
        this.options.break_on_count = conf.break_on_count; });
    this.metadata = opt.metadata;
    this.inited = !!this.metadata;
};
MP4BuilderStream.prototype = new Stream();
MP4BuilderStream.prototype.constructor = MP4BuilderStream;
MP4BuilderStream.prototype.push = function(packet){
    var id;
    if (packet.type=='metadata')
        return void (this.metadata = packet);
    if (packet.type=='seek')
    {
        for (id in this.tracks)
            this.tracks[id].samples = [];
        return;
    }
    id = packet.trackId;
    this.tracks[id] = this.tracks[id]||{samples: [], seqno: 0, sc: 0,
        type: packet.type};
    var sample = {
        duration: packet.duration,
        size: packet.size,
        dts: packet.dts,
        pts: packet.pts,
        data: new Uint8Array(packet.data),
        sn: packet.sn,
    };
    if (packet.type=='video')
    {
        var scale = 90000/packet.ts;
        sample.duration = Math.floor(sample.duration*scale);
        sample.pts = Math.floor(sample.pts*scale);
        sample.dts = Math.floor(sample.dts*scale);
        sample.flags = {
            dependsOn: (sample.dep&&sample.dep.dep)|0,
            isDependedOn: (sample.dep&&sample.dep.is_dep)|0,
            hasRedundancy: ((sample.dep&&sample.dep.red)|0) || 2*packet.synced,
            isLeading: (sample.dep&&sample.dep.lead)|0,
        };
        if (!this.options.break_on_count)
            sample.flags.isNonSyncSample = +!packet.synced;
        sample.compositionTimeOffset = sample.pts-sample.dts;
    }
    packet.data = null;
    this.tracks[id].samples.push(sample);
};
MP4BuilderStream.prototype.flush = function(){
    var moof, mdat, seg_sz, _this = this;
    if (!this.inited)
    {
        // build and emit init segments
        this.inited = true;
        var inits = [];
        this.metadata.tracks.forEach(function(tr){
            switch (tr.type)
            {
            case 'video':
                tr.profileIdc = tr.dr.avcc.avc_p_i;
                tr.levelIdc = tr.dr.avcc.avc_l_i;
                tr.profileCompatibility = tr.dr.avcc.prof_compat;
                tr.width = tr.track_width;
                tr.height = tr.track_height;
                tr.sps = tr.dr.avcc.sps.map(function(e){ return e.nal; });
                tr.pps = tr.dr.avcc.pps.map(function(e){ return e.nal; });
                break;
            case 'audio':
                tr.samplesize = tr.dr.s_size;
                tr.audioobjecttype = tr.dr.esds.aot;
                tr.samplingfrequencyindex = tr.dr.esds.freq;
                tr.channelcount = tr.dr.esds.channel;
            }
            inits.push({
                id: tr.id,
                buffer: mp4.initSegment([tr], _this.options),
            });
        });
        this.trigger('data', {init: true, inits: inits});
    }
    for (var id in this.tracks)
    {
        var track = this.tracks[id];
        if (!track.samples.length)
            continue;
        var seg_slice = track.samples;
        seg_sz = seg_slice.reduce(function(a, b){
            return a+b.data.length; }, 0);
        moof = mp4.moof(++track.seqno, [{
            id: id,
            baseMediaDecodeTime: seg_slice[0].dts,
            samples: seg_slice,
            type: track.type,
        }], _this.options);
        var segment = new Uint8Array(8+seg_sz+moof.length);
        var sd = new Uint8Array(seg_sz);
        var offset = 0;
        for (var i=0; i<seg_slice.length; i++)
        {
            sd.set(seg_slice[i].data, offset);
            offset += seg_slice[i].data.length;
        }
        mdat = mp4.mdat(sd);
        segment.set(moof);
        segment.set(mdat, moof.length);
        segment.sn = seg_slice[seg_slice.length-1].sn;
        sd = mdat = moof = null;
        this.tracks[id].sc += seg_slice.length;
        this.trigger('data', {id: id, data: segment,
            sc: this.tracks[id].sc});
        this.tracks[id].samples = [];
    }
    this.trigger('done');
};

module.exports = {
  MP4ParserStream: MP4ParserStream,
  AudioFilterStream: AudioFilterStream,
  VideoFilterStream: VideoFilterStream,
  MP4BuilderStream: MP4BuilderStream,
};

},{"../utils/stream.js":41,"./mp4-generator.js":35}],37:[function(_dereq_,module,exports){
/**
 * mux.js
 *
 * Copyright (c) 2015 Brightcove
 * All rights reserved.
 *
 * A stream-based mp2t to mp4 converter. This utility can be used to
 * deliver mp4s to a SourceBuffer on platforms that support native
 * Media Source Extensions.
 */
'use strict';

var Stream = _dereq_('../utils/stream.js');
var mp4 = _dereq_('./mp4-generator.js');
var mp4p = _dereq_('./mp4-parser.js');
var m2ts = _dereq_('../m2ts/m2ts.js');
var codecs = _dereq_('../codecs');
var AdtsStream = codecs.adts;
var H264Stream = codecs.h264.H264Stream;
var AacStream = _dereq_('../aac');

// constants
var AUDIO_PROPERTIES = [
  'audioobjecttype',
  'channelcount',
  'samplerate',
  'samplingfrequencyindex',
  'samplesize'
];

var VIDEO_PROPERTIES = [
  'width',
  'height',
  'profileIdc',
  'levelIdc',
  'profileCompatibility'
];

var ONE_SECOND_IN_TS = 90000; // 90kHz clock

// object types
var VideoSegmentStream, AudioSegmentStream, Transmuxer, CoalesceStream;

// Helper functions
var
  createDefaultSample,
  isLikelyAacData,
  collectDtsInfo,
  clearDtsInfo,
  calculateTrackBaseMediaDecodeTime,
  arrayEquals,
  sumFrameByteLengths;

/**
 * Default sample object
 * see ISO/IEC 14496-12:2012, section 8.6.4.3
 */
createDefaultSample = function() {
  return {
    size: 0,
    flags: {
      isLeading: 0,
      dependsOn: 1,
      isDependedOn: 0,
      hasRedundancy: 0,
      degradationPriority: 0
    }
  };
};

isLikelyAacData = function(data) {
  if ((data[0] === 'I'.charCodeAt(0)) &&
      (data[1] === 'D'.charCodeAt(0)) &&
      (data[2] === '3'.charCodeAt(0))) {
    return true;
  }
  return false;
};

/**
 * Compare two arrays (even typed) for same-ness
 */
arrayEquals = function(a, b) {
  var
    i;

  if (a.length !== b.length) {
    return false;
  }

  // compare the value of each element in the array
  for (i = 0; i < a.length; i++) {
    if (a[i] !== b[i]) {
      return false;
    }
  }

  return true;
};

/**
 * Sum the `byteLength` properties of the data in each AAC frame
 */
sumFrameByteLengths = function(array) {
  var
    i,
    currentObj,
    sum = 0;

  // sum the byteLength's all each nal unit in the frame
  for (i = 0; i < array.length; i++) {
    currentObj = array[i];
    sum += currentObj.data.byteLength;
  }

  return sum;
};

/**
 * Constructs a single-track, ISO BMFF media segment from AAC data
 * events. The output of this stream can be fed to a SourceBuffer
 * configured with a suitable initialization segment.
 */
AudioSegmentStream = function(track) {
  var
    adtsFrames = [],
    sequenceNumber = 0,
    earliestAllowedDts = 0;

  AudioSegmentStream.prototype.init.call(this);

  this.push = function(data) {
    collectDtsInfo(track, data);

    if (track) {
      AUDIO_PROPERTIES.forEach(function(prop) {
        track[prop] = data[prop];
      });
    }

    // buffer audio data until end() is called
    adtsFrames.push(data);
  };

  this.setEarliestDts = function(earliestDts) {
    earliestAllowedDts = earliestDts - track.timelineStartInfo.baseMediaDecodeTime;
  };

  this.flush = function() {
    var
      frames,
      moof,
      mdat,
      boxes;

    // return early if no audio data has been observed
    if (adtsFrames.length === 0) {
      this.trigger('done', 'AudioSegmentStream');
      return;
    }

    frames = this.trimAdtsFramesByEarliestDts_(adtsFrames);

    // we have to build the index from byte locations to
    // samples (that is, adts frames) in the audio data
    track.samples = this.generateSampleTable_(frames);

    // concatenate the audio data to constuct the mdat
    mdat = mp4.mdat(this.concatenateFrameData_(frames));

    adtsFrames = [];

    calculateTrackBaseMediaDecodeTime(track);
    moof = mp4.moof(sequenceNumber, [track]);
    boxes = new Uint8Array(moof.byteLength + mdat.byteLength);

    // bump the sequence number for next time
    sequenceNumber++;

    boxes.set(moof);
    boxes.set(mdat, moof.byteLength);

    clearDtsInfo(track);

    this.trigger('data', {track: track, boxes: boxes});
    this.trigger('done', 'AudioSegmentStream');
  };

  // If the audio segment extends before the earliest allowed dts
  // value, remove AAC frames until starts at or after the earliest
  // allowed DTS so that we don't end up with a negative baseMedia-
  // DecodeTime for the audio track
  this.trimAdtsFramesByEarliestDts_ = function(adtsFrames) {
    if (track.minSegmentDts >= earliestAllowedDts) {
      return adtsFrames;
    }

    // We will need to recalculate the earliest segment Dts
    track.minSegmentDts = Infinity;

    return adtsFrames.filter(function(currentFrame) {
      // If this is an allowed frame, keep it and record it's Dts
      if (currentFrame.dts >= earliestAllowedDts) {
        track.minSegmentDts = Math.min(track.minSegmentDts, currentFrame.dts);
        track.minSegmentPts = track.minSegmentDts;
        return true;
      }
      // Otherwise, discard it
      return false;
    });
  };

  // generate the track's raw mdat data from an array of frames
  this.generateSampleTable_ = function(frames) {
    var
      i,
      currentFrame,
      samples = [];

    for (i = 0; i < frames.length; i++) {
      currentFrame = frames[i];
      samples.push({
        size: currentFrame.data.byteLength,
        duration: 1024 // For AAC audio, all samples contain 1024 samples
      });
    }
    return samples;
  };

  // generate the track's sample table from an array of frames
  this.concatenateFrameData_ = function(frames) {
    var
      i,
      currentFrame,
      dataOffset = 0,
      data = new Uint8Array(sumFrameByteLengths(frames));

    for (i = 0; i < frames.length; i++) {
      currentFrame = frames[i];

      data.set(currentFrame.data, dataOffset);
      dataOffset += currentFrame.data.byteLength;
    }
    return data;
  };
};

AudioSegmentStream.prototype = new Stream();

/**
 * Constructs a single-track, ISO BMFF media segment from H264 data
 * events. The output of this stream can be fed to a SourceBuffer
 * configured with a suitable initialization segment.
 * @param track {object} track metadata configuration
 */
VideoSegmentStream = function(track) {
  var
    sequenceNumber = 0,
    nalUnits = [],
    config,
    pps;

  VideoSegmentStream.prototype.init.call(this);

  delete track.minPTS;

  this.gopCache_ = [];

  this.push = function(nalUnit) {
    collectDtsInfo(track, nalUnit);

    // record the track config
    if (nalUnit.nalUnitType === codecs.h264.unitTypes.seq_parameter_set_rbsp && !config) {
      config = nalUnit.config;
      track.sps = [nalUnit.data];
      VIDEO_PROPERTIES.forEach(function(prop) {
        track[prop] = config[prop];
      }, this);
    }

    if (nalUnit.nalUnitType === codecs.h264.unitTypes.pic_parameter_set_rbsp &&
        !pps) {
      pps = nalUnit.data;
      track.pps = [nalUnit.data];
    }

    // buffer video until flush() is called
    nalUnits.push(nalUnit);
  };

  this.flush = function() {
    var
      frames,
      gopForFusion,
      gops,
      moof,
      mdat,
      boxes;

    // Throw away nalUnits at the start of the byte stream until
    // we find the first AUD
    while (nalUnits.length && nalUnits[0].nalUnitType !== codecs.h264.unitTypes.access_unit_delimiter_rbsp) {
      nalUnits.shift();
    }

    // Return early if no video data has been observed
    if (nalUnits.length === 0) {
      this.resetStream_();
      this.trigger('done', 'VideoSegmentStream');
      return;
    }

    // Organize the raw nal-units into arrays that represent
    // higher-level constructs such as frames and gops
    // (group-of-pictures)
    frames = this.groupNalsIntoFrames_(nalUnits);
    gops = this.groupFramesIntoGops_(frames);

    // If the first frame of this fragment is not a keyframe we have
    // a problem since MSE (on Chrome) requires a leading keyframe.
    //
    // We have two approaches to repairing this situation:
    // 1) GOP-FUSION:
    //    This is where we keep track of the GOPS (group-of-pictures)
    //    from previous fragments and attempt to find one that we can
    //    prepend to the current fragment in order to create a valid
    //    fragment.
    // 2) KEYFRAME-PULLING:
    //    Here we search for the first keyframe in the fragment and
    //    throw away all the frames between the start of the fragment
    //    and that keyframe. We then extend the duration and pull the
    //    PTS of the keyframe forward so that it covers the time range
    //    of the frames that were disposed of.
    //
    // #1 is far prefereable over #2 which can cause "stuttering" but
    // requires more things to be just right.
    if (!gops[0][0].keyFrame) {
      // Search for a gop for fusion from our gopCache
      gopForFusion = this.getGopForFusion_(nalUnits[0], track);

      if (gopForFusion) {
        gops.unshift(gopForFusion);
        // Adjust Gops' metadata to account for the inclusion of the
        // new gop at the beginning
        gops.byteLength += gopForFusion.byteLength;
        gops.nalCount += gopForFusion.nalCount;
        gops.pts = gopForFusion.pts;
        gops.dts = gopForFusion.dts;
        gops.duration += gopForFusion.duration;
      } else {
        // If we didn't find a candidate gop fall back to keyrame-pulling
        gops = this.extendFirstKeyFrame_(gops);
      }
    }
    collectDtsInfo(track, gops);

    // First, we have to build the index from byte locations to
    // samples (that is, frames) in the video data
    track.samples = this.generateSampleTable_(gops);

    // Concatenate the video data and construct the mdat
    mdat = mp4.mdat(this.concatenateNalData_(gops));

    // save all the nals in the last GOP into the gop cache
    this.gopCache_.unshift({
      gop: gops.pop(),
      pps: track.pps,
      sps: track.sps
    });

    // Keep a maximum of 6 GOPs in the cache
    this.gopCache_.length = Math.min(6, this.gopCache_.length);

    // Clear nalUnits
    nalUnits = [];

    calculateTrackBaseMediaDecodeTime(track);

    this.trigger('timelineStartInfo', track.timelineStartInfo);

    moof = mp4.moof(sequenceNumber, [track]);

    // it would be great to allocate this array up front instead of
    // throwing away hundreds of media segment fragments
    boxes = new Uint8Array(moof.byteLength + mdat.byteLength);

    // Bump the sequence number for next time
    sequenceNumber++;

    boxes.set(moof);
    boxes.set(mdat, moof.byteLength);

    this.trigger('data', {track: track, boxes: boxes});

    this.resetStream_();

    // Continue with the flush process now
    this.trigger('done', 'VideoSegmentStream');
  };

  this.resetStream_ = function() {
    clearDtsInfo(track);

    // reset config and pps because they may differ across segments
    // for instance, when we are rendition switching
    config = undefined;
    pps = undefined;
  };

  // Search for a candidate Gop for gop-fusion from the gop cache and
  // return it or return null if no good candidate was found
  this.getGopForFusion_ = function(nalUnit) {
    var
      halfSecond = 45000, // Half-a-second in a 90khz clock
      allowableOverlap = 10000, // About 3 frames @ 30fps
      nearestDistance = Infinity,
      dtsDistance,
      nearestGopObj,
      currentGop,
      currentGopObj,
      i;

    // Search for the GOP nearest to the beginning of this nal unit
    for (i = 0; i < this.gopCache_.length; i++) {
      currentGopObj = this.gopCache_[i];
      currentGop = currentGopObj.gop;

      // Reject Gops with different SPS or PPS
      if (!(track.pps && arrayEquals(track.pps[0], currentGopObj.pps[0])) ||
          !(track.sps && arrayEquals(track.sps[0], currentGopObj.sps[0]))) {
        continue;
      }

      // Reject Gops that would require a negative baseMediaDecodeTime
      if (currentGop.dts < track.timelineStartInfo.dts) {
        continue;
      }

      // The distance between the end of the gop and the start of the nalUnit
      dtsDistance = (nalUnit.dts - currentGop.dts) - currentGop.duration;

      // Only consider GOPS that start before the nal unit and end within
      // a half-second of the nal unit
      if (dtsDistance >= -allowableOverlap &&
          dtsDistance <= halfSecond) {

        // Always use the closest GOP we found if there is more than
        // one candidate
        if (!nearestGopObj ||
            nearestDistance > dtsDistance) {
          nearestGopObj = currentGopObj;
          nearestDistance = dtsDistance;
        }
      }
    }

    if (nearestGopObj) {
      return nearestGopObj.gop;
    }
    return null;
  };

  this.extendFirstKeyFrame_ = function(gops) {
    var currentGop;

    if (!gops[0][0].keyFrame && gops.length > 1) {
      // Remove the first GOP
      currentGop = gops.shift();

      gops.byteLength -=  currentGop.byteLength;
      gops.nalCount -= currentGop.nalCount;

      // Extend the first frame of what is now the
      // first gop to cover the time period of the
      // frames we just removed
      gops[0][0].dts = currentGop.dts;
      gops[0][0].pts = currentGop.pts;
      gops[0][0].duration += currentGop.duration;
    }

    return gops;
  };

  // Convert an array of nal units into an array of frames with each frame being
  // composed of the nal units that make up that frame
  // Also keep track of cummulative data about the frame from the nal units such
  // as the frame duration, starting pts, etc.
  this.groupNalsIntoFrames_ = function(nalUnits) {
    var
      i,
      currentNal,
      currentFrame = [],
      frames = [];

    currentFrame.byteLength = 0;

    for (i = 0; i < nalUnits.length; i++) {
      currentNal = nalUnits[i];

      // Split on 'aud'-type nal units
      if (currentNal.nalUnitType === codecs.h264.unitTypes.access_unit_delimiter_rbsp) {
        // Since the very first nal unit is expected to be an AUD
        // only push to the frames array when currentFrame is not empty
        if (currentFrame.length) {
          currentFrame.duration = currentNal.dts - currentFrame.dts;
          frames.push(currentFrame);
        }
        currentFrame = [currentNal];
        currentFrame.byteLength = currentNal.data.byteLength;
        currentFrame.pts = currentNal.pts;
        currentFrame.dts = currentNal.dts;
      } else {
        // Specifically flag key frames for ease of use later
        if (currentNal.nalUnitType === codecs.h264.unitTypes.slice_layer_without_partitioning_rbsp_idr) {
          currentFrame.keyFrame = true;
        }
        currentFrame.duration = currentNal.dts - currentFrame.dts;
        currentFrame.byteLength += currentNal.data.byteLength;
        currentFrame.push(currentNal);
      }
    }

    // For the last frame, use the duration of the previous frame if we
    // have nothing better to go on
    if (frames.length &&
        (!currentFrame.duration ||
         currentFrame.duration <= 0)) {
      currentFrame.duration = frames[frames.length - 1].duration;
    }

    // Push the final frame
    frames.push(currentFrame);
    return frames;
  };

  // Convert an array of frames into an array of Gop with each Gop being composed
  // of the frames that make up that Gop
  // Also keep track of cummulative data about the Gop from the frames such as the
  // Gop duration, starting pts, etc.
  this.groupFramesIntoGops_ = function(frames) {
    var
      i,
      currentFrame,
      currentGop = [],
      gops = [];

    // We must pre-set some of the values on the Gop since we
    // keep running totals of these values
    currentGop.byteLength = 0;
    currentGop.nalCount = 0;
    currentGop.duration = 0;
    currentGop.pts = frames[0].pts;
    currentGop.dts = frames[0].dts;

    // store some metadata about all the Gops
    gops.byteLength = 0;
    gops.nalCount = 0;
    gops.duration = 0;
    gops.pts = frames[0].pts;
    gops.dts = frames[0].dts;

    for (i = 0; i < frames.length; i++) {
      currentFrame = frames[i];

      if (currentFrame.keyFrame) {
        // Since the very first frame is expected to be an keyframe
        // only push to the gops array when currentGop is not empty
        if (currentGop.length) {
          gops.push(currentGop);
          gops.byteLength += currentGop.byteLength;
          gops.nalCount += currentGop.nalCount;
          gops.duration += currentGop.duration;
        }

        currentGop = [currentFrame];
        currentGop.nalCount = currentFrame.length;
        currentGop.byteLength = currentFrame.byteLength;
        currentGop.pts = currentFrame.pts;
        currentGop.dts = currentFrame.dts;
        currentGop.duration = currentFrame.duration;
      } else {
        currentGop.duration += currentFrame.duration;
        currentGop.nalCount += currentFrame.length;
        currentGop.byteLength += currentFrame.byteLength;
        currentGop.push(currentFrame);
      }
    }

    if (gops.length && currentGop.duration <= 0) {
      currentGop.duration = gops[gops.length - 1].duration;
    }
    gops.byteLength += currentGop.byteLength;
    gops.nalCount += currentGop.nalCount;
    gops.duration += currentGop.duration;

    // push the final Gop
    gops.push(currentGop);
    return gops;
  };

  // generate the track's sample table from an array of gops
  this.generateSampleTable_ = function(gops, baseDataOffset) {
    var
      h, i,
      sample,
      currentGop,
      currentFrame,
      dataOffset = baseDataOffset || 0,
      samples = [];

    for (h = 0; h < gops.length; h++) {
      currentGop = gops[h];

      for (i = 0; i < currentGop.length; i++) {
        currentFrame = currentGop[i];

        sample = createDefaultSample();

        sample.dataOffset = dataOffset;
        sample.compositionTimeOffset = currentFrame.pts - currentFrame.dts;
        sample.duration = currentFrame.duration;
        sample.size = 4 * currentFrame.length; // Space for nal unit size
        sample.size += currentFrame.byteLength;

        if (currentFrame.keyFrame) {
          sample.flags.dependsOn = 2;
        }

        dataOffset += sample.size;

        samples.push(sample);
      }
    }
    return samples;
  };

  // generate the track's raw mdat data from an array of gops
  this.concatenateNalData_ = function(gops) {
    var
      h, i, j,
      currentGop,
      currentFrame,
      currentNal,
      dataOffset = 0,
      nalsByteLength = gops.byteLength,
      numberOfNals = gops.nalCount,
      totalByteLength = nalsByteLength + 4 * numberOfNals,
      data = new Uint8Array(totalByteLength),
      view = new DataView(data.buffer);

    // For each Gop..
    for (h = 0; h < gops.length; h++) {
      currentGop = gops[h];

      // For each Frame..
      for (i = 0; i < currentGop.length; i++) {
        currentFrame = currentGop[i];

        // For each NAL..
        for (j = 0; j < currentFrame.length; j++) {
          currentNal = currentFrame[j];

          view.setUint32(dataOffset, currentNal.data.byteLength);
          dataOffset += 4;
          data.set(currentNal.data, dataOffset);
          dataOffset += currentNal.data.byteLength;
        }
      }
    }
    return data;
  };
};

VideoSegmentStream.prototype = new Stream();

/**
 * Store information about the start and end of the track and the
 * duration for each frame/sample we process in order to calculate
 * the baseMediaDecodeTime
 */
collectDtsInfo = function(track, data) {
  if (typeof data.pts === 'number') {
    if (track.timelineStartInfo.pts === undefined) {
      track.timelineStartInfo.pts = data.pts;
    }

    if (track.minSegmentPts === undefined) {
      track.minSegmentPts = data.pts;
    } else {
      track.minSegmentPts = Math.min(track.minSegmentPts, data.pts);
    }

    if (track.maxSegmentPts === undefined) {
      track.maxSegmentPts = data.pts;
    } else {
      track.maxSegmentPts = Math.max(track.maxSegmentPts, data.pts);
    }
  }

  if (typeof data.dts === 'number') {
    if (track.timelineStartInfo.dts === undefined) {
      track.timelineStartInfo.dts = data.dts;
    }

    if (track.minSegmentDts === undefined) {
      track.minSegmentDts = data.dts;
    } else {
      track.minSegmentDts = Math.min(track.minSegmentDts, data.dts);
    }

    if (track.maxSegmentDts === undefined) {
      track.maxSegmentDts = data.dts;
    } else {
      track.maxSegmentDts = Math.max(track.maxSegmentDts, data.dts);
    }
  }
};

/**
 * Clear values used to calculate the baseMediaDecodeTime between
 * tracks
 */
clearDtsInfo = function(track) {
  delete track.minSegmentDts;
  delete track.maxSegmentDts;
  delete track.minSegmentPts;
  delete track.maxSegmentPts;
};

/**
 * Calculate the track's baseMediaDecodeTime based on the earliest
 * DTS the transmuxer has ever seen and the minimum DTS for the
 * current track
 */
calculateTrackBaseMediaDecodeTime = function(track) {
  var
    oneSecondInPTS = 90000, // 90kHz clock
    scale,
    // Calculate the distance, in time, that this segment starts from the start
    // of the timeline (earliest time seen since the transmuxer initialized)
    timeSinceStartOfTimeline = track.minSegmentDts - track.timelineStartInfo.dts,
    // Calculate the first sample's effective compositionTimeOffset
    firstSampleCompositionOffset = track.minSegmentPts - track.minSegmentDts;

  // track.timelineStartInfo.baseMediaDecodeTime is the location, in time, where
  // we want the start of the first segment to be placed
  track.baseMediaDecodeTime = track.timelineStartInfo.baseMediaDecodeTime;

  // Add to that the distance this segment is from the very first
  track.baseMediaDecodeTime += timeSinceStartOfTimeline;

  // Subtract this segment's "compositionTimeOffset" so that the first frame of
  // this segment is displayed exactly at the `baseMediaDecodeTime` or at the
  // end of the previous segment
  track.baseMediaDecodeTime -= firstSampleCompositionOffset;

  // baseMediaDecodeTime must not become negative
  track.baseMediaDecodeTime = Math.max(0, track.baseMediaDecodeTime);

  if (track.type === 'audio') {
    // Audio has a different clock equal to the sampling_rate so we need to
    // scale the PTS values into the clock rate of the track
    scale = track.samplerate / oneSecondInPTS;
    track.baseMediaDecodeTime *= scale;
    track.baseMediaDecodeTime = Math.floor(track.baseMediaDecodeTime);
  }

  return track.baseMediaDecodeTime;
};

/**
 * A Stream that can combine multiple streams (ie. audio & video)
 * into a single output segment for MSE. Also supports audio-only
 * and video-only streams.
 */
CoalesceStream = function(options, metadataStream) {
  // Number of Tracks per output segment
  // If greater than 1, we combine multiple
  // tracks into a single segment
  this.numberOfTracks = 0;
  this.metadataStream = metadataStream;

  if (typeof options.remux !== 'undefined') {
    this.remuxTracks = !!options.remux;
  } else {
    this.remuxTracks = true;
  }

  this.pendingTracks = [];
  this.videoTrack = null;
  this.pendingBoxes = [];
  this.pendingCaptions = [];
  this.pendingMetadata = [];
  this.pendingBytes = 0;
  this.emittedTracks = 0;

  CoalesceStream.prototype.init.call(this);

  // Take output from multiple
  this.push = function(output) {
    // buffer incoming captions until the associated video segment
    // finishes
    if (output.text) {
      return this.pendingCaptions.push(output);
    }
    // buffer incoming id3 tags until the final flush
    if (output.frames) {
      return this.pendingMetadata.push(output);
    }

    // Add this track to the list of pending tracks and store
    // important information required for the construction of
    // the final segment
    this.pendingTracks.push(output.track);
    this.pendingBoxes.push(output.boxes);
    this.pendingBytes += output.boxes.byteLength;

    if (output.track.type === 'video') {
      this.videoTrack = output.track;
    }
    if (output.track.type === 'audio') {
      this.audioTrack = output.track;
    }
  };
};

CoalesceStream.prototype = new Stream();
CoalesceStream.prototype.flush = function(flushSource) {
  var
    offset = 0,
    event = {
      captions: [],
      metadata: [],
      info: {}
    },
    caption,
    id3,
    initSegment,
    timelineStartPts = 0,
    i;

  if (this.pendingTracks.length < this.numberOfTracks) {
    if (flushSource !== 'VideoSegmentStream' &&
        flushSource !== 'AudioSegmentStream') {
      // Return because we haven't received a flush from a data-generating
      // portion of the segment (meaning that we have only recieved meta-data
      // or captions.)
      return;
    } else if (this.remuxTracks) {
      // Return until we have enough tracks from the pipeline to remux (if we
      // are remuxing audio and video into a single MP4)
      return;
    } else if (this.pendingTracks.length === 0) {
      // In the case where we receive a flush without any data having been
      // received we consider it an emitted track for the purposes of coalescing
      // `done` events.
      // We do this for the case where there is an audio and video track in the
      // segment but no audio data. (seen in several playlists with alternate
      // audio tracks and no audio present in the main TS segments.)
      this.emittedTracks++;

      if (this.emittedTracks >= this.numberOfTracks) {
        this.trigger('done');
        this.emittedTracks = 0;
      }
      return;
    }
  }

  if (this.videoTrack) {
    timelineStartPts = this.videoTrack.timelineStartInfo.pts;
    VIDEO_PROPERTIES.forEach(function(prop) {
      event.info[prop] = this.videoTrack[prop];
    }, this);
  } else if (this.audioTrack) {
    timelineStartPts = this.audioTrack.timelineStartInfo.pts;
    AUDIO_PROPERTIES.forEach(function(prop) {
      event.info[prop] = this.audioTrack[prop];
    }, this);
  }

  if (this.pendingTracks.length === 1) {
    event.type = this.pendingTracks[0].type;
  } else {
    event.type = 'combined';
  }

  this.emittedTracks += this.pendingTracks.length;

  initSegment = mp4.initSegment(this.pendingTracks, this.options);
  this.pendingBytes += initSegment.byteLength;
  this.pendingBoxes.unshift(initSegment);

  // Create a new typed array large enough to hold the init
  // segment and all tracks
  event.data = new Uint8Array(this.pendingBytes);

  // Append each moof+mdat (one per track) after the init segment
  for (i = 0; i < this.pendingBoxes.length; i++) {
    event.data.set(this.pendingBoxes[i], offset);
    offset += this.pendingBoxes[i].byteLength;
  }

  // Translate caption PTS times into second offsets into the
  // video timeline for the segment
  for (i = 0; i < this.pendingCaptions.length; i++) {
    caption = this.pendingCaptions[i];
    caption.startTime = (caption.startPts - timelineStartPts);
    caption.startTime /= 90e3;
    caption.endTime = (caption.endPts - timelineStartPts);
    caption.endTime /= 90e3;
    event.captions.push(caption);
  }

  // Translate ID3 frame PTS times into second offsets into the
  // video timeline for the segment
  for (i = 0; i < this.pendingMetadata.length; i++) {
    id3 = this.pendingMetadata[i];
    id3.cueTime = (id3.pts - timelineStartPts);
    id3.cueTime /= 90e3;
    event.metadata.push(id3);
  }
  // We add this to every single emitted segment even though we only need
  // it for the first
  event.metadata.dispatchType = this.metadataStream.dispatchType;

  // Reset stream state
  this.pendingTracks.length = 0;
  this.videoTrack = null;
  this.pendingBoxes.length = 0;
  this.pendingCaptions.length = 0;
  this.pendingBytes = 0;
  this.pendingMetadata.length = 0;

  // Emit the built segment
  this.trigger('data', event);

  // Only emit `done` if all tracks have been flushed and emitted
  if (this.emittedTracks >= this.numberOfTracks) {
    this.trigger('done');
    this.emittedTracks = 0;
  }
};
/**
 * A Stream that expects MP2T binary data as input and produces
 * corresponding media segments, suitable for use with Media Source
 * Extension (MSE) implementations that support the ISO BMFF byte
 * stream format, like Chrome.
 */
Transmuxer = function(options) {
  var
    self = this,
    hasFlushed = true,
    videoTrack,
    audioTrack;

  Transmuxer.prototype.init.call(this);

  options = options || {};
  options.input_type = options.input_type||'ts';
  if (options.break_on_count===undefined) {
    options.break_on_count = true;
  }
  this.baseMediaDecodeTime = options.baseMediaDecodeTime || 0;
  this.transmuxPipeline_ = {};

  this.setupAacPipeline = function() {
    var pipeline = {};
    this.transmuxPipeline_ = pipeline;

    pipeline.type = 'aac';
    pipeline.metadataStream = new m2ts.MetadataStream();

    // set up the parsing pipeline
    pipeline.aacStream = new AacStream();
    pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
    pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
    pipeline.adtsStream = new AdtsStream();
    pipeline.coalesceStream = new CoalesceStream(options, pipeline.metadataStream);
    pipeline.headOfPipeline = pipeline.aacStream;

    pipeline.aacStream
      .pipe(pipeline.audioTimestampRolloverStream)
      .pipe(pipeline.adtsStream);
    pipeline.aacStream
      .pipe(pipeline.timedMetadataTimestampRolloverStream)
      .pipe(pipeline.metadataStream)
      .pipe(pipeline.coalesceStream);

    pipeline.metadataStream.on('timestamp', function(frame) {
      pipeline.aacStream.setTimestamp(frame.timeStamp);
    });

    pipeline.aacStream.on('data', function(data) {
      if (data.type === 'timed-metadata' && !pipeline.audioSegmentStream) {
        audioTrack = audioTrack || {
          timelineStartInfo: {
            baseMediaDecodeTime: self.baseMediaDecodeTime
          },
          codec: 'adts',
          type: 'audio'
        };
        // hook up the audio segment stream to the first track with aac data
        pipeline.coalesceStream.numberOfTracks++;
        pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);
        // Set up the final part of the audio pipeline
        pipeline.adtsStream
          .pipe(pipeline.audioSegmentStream)
          .pipe(pipeline.coalesceStream);
      }
    });

    // Re-emit any data coming from the coalesce stream to the outside world
    pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
    // Let the consumer know we have finished flushing the entire pipeline
    pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
  };

  this.setupTsPipeline = function() {
    var pipeline = {};
    this.transmuxPipeline_ = pipeline;

    pipeline.type = options.input_type;
    if (pipeline.type=='ts')
    {
      options.metadataStream = pipeline.metadataStream =
        new m2ts.MetadataStream();
      // set up the parsing pipeline
      pipeline.packetStream = new m2ts.TransportPacketStream();
      pipeline.parseStream = new m2ts.TransportParseStream();
      pipeline.elementaryStream = new m2ts.ElementaryStream();
      pipeline.videoTimestampRolloverStream = new m2ts.TimestampRolloverStream('video');
      pipeline.audioTimestampRolloverStream = new m2ts.TimestampRolloverStream('audio');
      pipeline.timedMetadataTimestampRolloverStream = new m2ts.TimestampRolloverStream('timed-metadata');
      pipeline.adtsStream = new AdtsStream();
      pipeline.h264Stream = new H264Stream();
      pipeline.captionStream = new m2ts.CaptionStream();
      pipeline.coalesceStream = new CoalesceStream(options,
        pipeline.metadataStream);
      pipeline.headOfPipeline = pipeline.packetStream;
      // disassemble MPEG2-TS packets into elementary streams
      pipeline.packetStream.pipe(pipeline.parseStream)
        .pipe(pipeline.elementaryStream);
      // !!THIS ORDER IS IMPORTANT!!
      // demux the streams
      pipeline.elementaryStream
        .pipe(pipeline.videoTimestampRolloverStream)
        .pipe(pipeline.h264Stream);
      pipeline.elementaryStream
        .pipe(pipeline.audioTimestampRolloverStream)
        .pipe(pipeline.adtsStream);

      pipeline.elementaryStream
        .pipe(pipeline.timedMetadataTimestampRolloverStream)
        .pipe(pipeline.metadataStream)
        .pipe(pipeline.coalesceStream);
      // Hook up CEA-608/708 caption stream
      pipeline.h264Stream.pipe(pipeline.captionStream)
        .pipe(pipeline.coalesceStream);
    }
    else
    {
      pipeline.headOfPipeline = pipeline.elementaryStream =
        new mp4p.MP4ParserStream(options);
      pipeline.mp4BuilderStream = new mp4p.MP4BuilderStream(options);
      pipeline.elementaryStream.pipe(pipeline.mp4BuilderStream);
      this.seek = function(pos, ss){
        return pipeline.elementaryStream.seek(pos, ss); };
      this.get_tl = function(id){
        return pipeline.elementaryStream.get_tl(id); };
      this.conf_update = function(conf){
        pipeline.elementaryStream.trigger('confupdate', conf);
        pipeline.mp4BuilderStream.trigger('confupdate', conf);
      };
    }

    pipeline.elementaryStream.on('data', function(data) {
      var i;

      if (data.type === 'metadata') {
        if (options.input_type === 'mp4') {
          return void self.trigger('metadata', data);
        }
        i = data.tracks.length;

        // scan the tracks listed in the metadata
        while (i--) {
          if (!videoTrack && data.tracks[i].type === 'video') {
            videoTrack = data.tracks[i];
            videoTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
          } else if (!audioTrack && data.tracks[i].type === 'audio') {
            audioTrack = data.tracks[i];
            audioTrack.timelineStartInfo.baseMediaDecodeTime = self.baseMediaDecodeTime;
          }
        }

        // hook up the video segment stream to the first track with h264 data
        if (videoTrack && !pipeline.videoSegmentStream) {
          pipeline.coalesceStream.numberOfTracks++;
          pipeline.videoSegmentStream = new VideoSegmentStream(videoTrack);

          pipeline.videoSegmentStream.on('timelineStartInfo', function(timelineStartInfo) {
          // When video emits timelineStartInfo data after a flush, we forward that
          // info to the AudioSegmentStream, if it exists, because video timeline
          // data takes precedence.
            if (audioTrack) {
              audioTrack.timelineStartInfo = timelineStartInfo;
              // On the first segment we trim AAC frames that exist before the
              // very earliest DTS we have seen in video because Chrome will
              // interpret any video track with a baseMediaDecodeTime that is
              // non-zero as a gap.
              pipeline.audioSegmentStream.setEarliestDts(timelineStartInfo.dts);
            }
          });

          // Set up the final part of the video pipeline
          pipeline.h264Stream
            .pipe(pipeline.videoSegmentStream)
            .pipe(pipeline.coalesceStream);
        }

        if (audioTrack && !pipeline.audioSegmentStream) {
          // hook up the audio segment stream to the first track with aac data
          pipeline.coalesceStream.numberOfTracks++;
          pipeline.audioSegmentStream = new AudioSegmentStream(audioTrack);

          // Set up the final part of the audio pipeline
          pipeline.adtsStream
            .pipe(pipeline.audioSegmentStream)
            .pipe(pipeline.coalesceStream);
        }
      }
    });
    if (options.input_type==='mp4')
    {
      pipeline.mp4BuilderStream.on('data', this.trigger.bind(this, 'data'));
      pipeline.mp4BuilderStream.on('done', this.trigger.bind(this, 'done'));
    }
    else
    {
      // Re-emit any data coming from the coalesce stream to the outside world
      pipeline.coalesceStream.on('data', this.trigger.bind(this, 'data'));
      // Let the consumer know we have finished flushing the entire pipeline
      pipeline.coalesceStream.on('done', this.trigger.bind(this, 'done'));
    }
  };

  // hook up the segment streams once track metadata is delivered
  this.setBaseMediaDecodeTime = function (baseMediaDecodeTime) {
    var pipeline = this.transmuxPipeline_;

    this.baseMediaDecodeTime = baseMediaDecodeTime;
    if (audioTrack) {
      audioTrack.timelineStartInfo.dts = undefined;
      audioTrack.timelineStartInfo.pts = undefined;
      clearDtsInfo(audioTrack);
      audioTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
    }
    // XXX pavelki: to check how to act if mp4
    if (videoTrack) {
      if (pipeline.videoSegmentStream) {
        pipeline.videoSegmentStream.gopCache_ = [];
      }
      videoTrack.timelineStartInfo.dts = undefined;
      videoTrack.timelineStartInfo.pts = undefined;
      clearDtsInfo(videoTrack);
      videoTrack.timelineStartInfo.baseMediaDecodeTime = baseMediaDecodeTime;
    }
  };

  // feed incoming data to the front of the parsing pipeline
  this.push = function(data) {
    if (hasFlushed) {
      var isAac = isLikelyAacData(data) && options.input_type!='mp4';

      if (isAac && this.transmuxPipeline_.type !== 'aac') {
        this.setupAacPipeline();
      }
      else if (!isAac && this.transmuxPipeline_.type !== options.input_type)
      {
        this.setupTsPipeline();
      }
      hasFlushed = false;
    }
    return this.transmuxPipeline_.headOfPipeline.push(data);
  };

  this.appendBuffer = function(data) {
      return this.push(new Uint8Array(data));
  };

  // flush any buffered data
  this.flush = function() {
    hasFlushed = true;
    // Start at the top of the pipeline and flush all pending work
    this.transmuxPipeline_.headOfPipeline.flush();
  };
};
Transmuxer.prototype = new Stream();

module.exports = {
  Transmuxer: Transmuxer,
  VideoSegmentStream: VideoSegmentStream,
  AudioSegmentStream: AudioSegmentStream,
  AUDIO_PROPERTIES: AUDIO_PROPERTIES,
  VIDEO_PROPERTIES: VIDEO_PROPERTIES
};

},{"../aac":20,"../codecs":23,"../m2ts/m2ts.js":30,"../utils/stream.js":41,"./mp4-generator.js":35,"./mp4-parser.js":36}],38:[function(_dereq_,module,exports){
'use strict';

var
  tagTypes = {
    0x08: 'audio',
    0x09: 'video',
    0x12: 'metadata'
  },
  hex = function (val) {
    return '0x' + ('00' + val.toString(16)).slice(-2).toUpperCase();
  },
  hexStringList = function (data) {
    var arr = [], i;
    /* jshint -W086 */
    while(data.byteLength > 0) {
      i = 0;
      switch(data.byteLength) {
        default:
          arr.push(hex(data[i++]));
        case 7:
          arr.push(hex(data[i++]));
        case 6:
          arr.push(hex(data[i++]));
        case 5:
          arr.push(hex(data[i++]));
        case 4:
          arr.push(hex(data[i++]));
        case 3:
          arr.push(hex(data[i++]));
        case 2:
          arr.push(hex(data[i++]));
        case 1:
          arr.push(hex(data[i++]));
      }
      data = data.subarray(i);
    }
    /* jshint +W086 */
    return arr.join(' ');
  },
  parseAVCTag = function (tag, obj) {
    var
      avcPacketTypes = [
        'AVC Sequence Header',
        'AVC NALU',
        'AVC End-of-Sequence'
      ],
      nalUnitTypes = [
        'unspecified',
        'slice_layer_without_partitioning',
        'slice_data_partition_a_layer',
        'slice_data_partition_b_layer',
        'slice_data_partition_c_layer',
        'slice_layer_without_partitioning_idr',
        'sei',
        'seq_parameter_set',
        'pic_parameter_set',
        'access_unit_delimiter',
        'end_of_seq',
        'end_of_stream',
        'filler',
        'seq_parameter_set_ext',
        'prefix_nal_unit',
        'subset_seq_parameter_set',
        'reserved',
        'reserved',
        'reserved'
      ],
      compositionTime = (tag[1] & parseInt('01111111', 2) << 16) | (tag[2] << 8) | tag[3];

    obj = obj || {};

    obj.avcPacketType = avcPacketTypes[tag[0]];
    obj.CompositionTime = (tag[1] & parseInt('10000000', 2)) ? -compositionTime : compositionTime;

    if (tag[0] === 1) {
      obj.nalUnitTypeRaw = hexStringList(tag.subarray(4, 100));
    } else {
      obj.data = hexStringList(tag.subarray(4));
    }

    return obj;
  },
  parseVideoTag = function (tag, obj) {
    var
      frameTypes = [
        'Unknown',
        'Keyframe (for AVC, a seekable frame)',
        'Inter frame (for AVC, a nonseekable frame)',
        'Disposable inter frame (H.263 only)',
        'Generated keyframe (reserved for server use only)',
        'Video info/command frame'
      ],
      codecIDs = [
        'JPEG (currently unused)',
        'Sorenson H.263',
        'Screen video',
        'On2 VP6',
        'On2 VP6 with alpha channel',
        'Screen video version 2',
        'AVC'
      ],
      codecID = tag[0] & parseInt('00001111', 2);

    obj = obj || {};

    obj.frameType = frameTypes[(tag[0] & parseInt('11110000', 2)) >>> 4];
    obj.codecID = codecID;

    if (codecID === 7) {
      return parseAVCTag(tag.subarray(1), obj);
    }
    return obj;
  },
  parseAACTag = function (tag, obj) {
    var packetTypes = [
      'AAC Sequence Header',
      'AAC Raw'
    ];

    obj = obj || {};

    obj.aacPacketType = packetTypes[tag[0]];
    obj.data = hexStringList(tag.subarray(1));

    return obj;
  },
  parseAudioTag = function (tag, obj) {
    var
      formatTable = [
        'Linear PCM, platform endian',
        'ADPCM',
        'MP3',
        'Linear PCM, little endian',
        'Nellymoser 16-kHz mono',
        'Nellymoser 8-kHz mono',
        'Nellymoser',
        'G.711 A-law logarithmic PCM',
        'G.711 mu-law logarithmic PCM',
        'reserved',
        'AAC',
        'Speex',
        'MP3 8-Khz',
        'Device-specific sound'
      ],
      samplingRateTable = [
        '5.5-kHz',
        '11-kHz',
        '22-kHz',
        '44-kHz'
      ],
      soundFormat = (tag[0] & parseInt('11110000', 2)) >>> 4;

    obj = obj || {};

    obj.soundFormat = formatTable[soundFormat];
    obj.soundRate = samplingRateTable[(tag[0] & parseInt('00001100', 2)) >>> 2];
    obj.soundSize = ((tag[0] & parseInt('00000010', 2)) >>> 1) ? '16-bit' : '8-bit';
    obj.soundType = (tag[0] & parseInt('00000001', 2)) ? 'Stereo' : 'Mono';

    if (soundFormat === 10) {
      return parseAACTag(tag.subarray(1), obj);
    }
    return obj;
  },
  parseGenericTag = function (tag) {
    return {
      tagType: tagTypes[tag[0]],
      dataSize: (tag[1] << 16) | (tag[2] << 8) | tag[3],
      timestamp: (tag[7] << 24) | (tag[4] << 16) | (tag[5] << 8) | tag[6],
      streamID: (tag[8] << 16) | (tag[9] << 8) | tag[10]
    };
  },
  inspectFlvTag = function (tag) {
    var header = parseGenericTag(tag);
    switch (tag[0]) {
      case 0x08:
        parseAudioTag(tag.subarray(11), header);
        break;
      case 0x09:
        parseVideoTag(tag.subarray(11), header);
        break;
      case 0x12:
    }
    return header;
  },
  inspectFlv = function (bytes) {
    var i = 9, // header
        dataSize,
        parsedResults = [],
        tag;

    // traverse the tags
    i += 4; // skip previous tag size
    while (i < bytes.byteLength) {
      dataSize = bytes[i + 1] << 16;
      dataSize |= bytes[i + 2] << 8;
      dataSize |= bytes[i + 3];
      dataSize += 11;

      tag = bytes.subarray(i, i + dataSize);
      parsedResults.push(inspectFlvTag(tag));
      i += dataSize + 4;
    }
    return parsedResults;
  },
  textifyFlv = function (flvTagArray) {
    return JSON.stringify(flvTagArray, null, 2);
  };

module.exports = {
  inspectTag: inspectFlvTag,
  inspect: inspectFlv,
  textify: textifyFlv
};

},{}],39:[function(_dereq_,module,exports){
(function (global){
'use strict';

var
  inspectMp4,
  textifyMp4,
  /**
   * Returns the string representation of an ASCII encoded four byte buffer.
   * @param buffer {Uint8Array} a four-byte buffer to translate
   * @return {string} the corresponding string
   */
  parseType = function(buffer) {
    var result = '';
    result += String.fromCharCode(buffer[0]);
    result += String.fromCharCode(buffer[1]);
    result += String.fromCharCode(buffer[2]);
    result += String.fromCharCode(buffer[3]);
    return result;
  },
  parseMp4Date = function(seconds) {
    return new Date(seconds * 1000 - 2082844800000);
  },
  parseSampleFlags = function(flags) {
    return {
      isLeading: (flags[0] & 0x0c) >>> 2,
      dependsOn: flags[0] & 0x03,
      isDependedOn: (flags[1] & 0xc0) >>> 6,
      hasRedundancy: (flags[1] & 0x30) >>> 4,
      paddingValue: (flags[1] & 0x0e) >>> 1,
      isNonSyncSample: flags[1] & 0x01,
      degradationPriority: (flags[2] << 8) | flags[3]
    };
  },
  nalParse = function(avcStream) {
    var
      avcView = new DataView(avcStream.buffer, avcStream.byteOffset, avcStream.byteLength),
      result = [],
      i,
      length;
    for (i = 0; i + 4 < avcStream.length; i += length) {
      length = avcView.getUint32(i);
      i += 4;

      // bail if this doesn't appear to be an H264 stream
      if (length <= 0) {
        result.push('<span style=\'color:red;\'>MALFORMED DATA</span>');
        continue;
      }

      switch(avcStream[i] & 0x1F) {
      case 0x01:
        result.push('slice_layer_without_partitioning_rbsp');
        break;
      case 0x05:
        result.push('slice_layer_without_partitioning_rbsp_idr');
        break;
      case 0x06:
        result.push('sei_rbsp');
        break;
      case 0x07:
        result.push('seq_parameter_set_rbsp');
        break;
      case 0x08:
        result.push('pic_parameter_set_rbsp');
        break;
      case 0x09:
        result.push('access_unit_delimiter_rbsp');
        break;
      default:
        result.push('UNKNOWN NAL - ' + avcStream[i] & 0x1F);
        break;
      }
    }
    return result;
  },

  // registry of handlers for individual mp4 box types
  parse = {
    // codingname, not a first-class box type. stsd entries share the
    // same format as real boxes so the parsing infrastructure can be
    // shared
    avc1: function(data) {
      var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
      return {
        dataReferenceIndex: view.getUint16(6),
        width:  view.getUint16(24),
        height: view.getUint16(26),
        horizresolution: view.getUint16(28) + (view.getUint16(30) / 16),
        vertresolution: view.getUint16(32) + (view.getUint16(34) / 16),
        frameCount: view.getUint16(40),
        depth: view.getUint16(74),
        config: inspectMp4(data.subarray(78, data.byteLength))
      };
    },
    avcC: function(data) {
      var
        view = new DataView(data.buffer, data.byteOffset, data.byteLength),
        result = {
          configurationVersion: data[0],
          avcProfileIndication: data[1],
          profileCompatibility: data[2],
          avcLevelIndication: data[3],
          lengthSizeMinusOne: data[4] & 0x03,
          sps: [],
          pps: []
        },
        numOfSequenceParameterSets = data[5] & 0x1f,
        numOfPictureParameterSets,
        nalSize,
        offset,
        i;

      // iterate past any SPSs
      offset = 6;
      for (i = 0; i < numOfSequenceParameterSets; i++) {
        nalSize = view.getUint16(offset);
        offset += 2;
        result.sps.push(new Uint8Array(data.subarray(offset, offset + nalSize)));
        offset += nalSize;
      }
      // iterate past any PPSs
      numOfPictureParameterSets = data[offset];
      offset++;
      for (i = 0; i < numOfPictureParameterSets; i++) {
        nalSize = view.getUint16(offset);
        offset += 2;
        result.pps.push(new Uint8Array(data.subarray(offset, offset + nalSize)));
        offset += nalSize;
      }
      return result;
    },
    btrt: function(data) {
      var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
      return {
        bufferSizeDB: view.getUint32(0),
        maxBitrate: view.getUint32(4),
        avgBitrate: view.getUint32(8)
      };
    },
    esds: function(data) {
      return {
        version: data[0],
        flags: new Uint8Array(data.subarray(1, 4)),
        esId: (data[6] << 8) | data[7],
        streamPriority: data[8] & 0x1f,
        decoderConfig: {
          objectProfileIndication: data[11],
          streamType: (data[12] >>> 2) & 0x3f,
          bufferSize: (data[13] << 16) | (data[14] << 8) | data[15],
          maxBitrate: (data[16] << 24) |
            (data[17] << 16) |
            (data[18] <<  8) |
            data[19],
          avgBitrate: (data[20] << 24) |
            (data[21] << 16) |
            (data[22] <<  8) |
            data[23],
          decoderConfigDescriptor: {
            tag: data[24],
            length: data[25],
            audioObjectType: (data[26] >>> 3) & 0x1f,
            samplingFrequencyIndex: ((data[26] & 0x07) << 1) |
              ((data[27] >>> 7) & 0x01),
            channelConfiguration: (data[27] >>> 3) & 0x0f
          }
        }
      };
    },
    ftyp: function(data) {
      var
        view = new DataView(data.buffer, data.byteOffset, data.byteLength),
        result = {
          majorBrand: parseType(data.subarray(0, 4)),
          minorVersion: view.getUint32(4),
          compatibleBrands: []
        },
        i = 8;
      while (i < data.byteLength) {
        result.compatibleBrands.push(parseType(data.subarray(i, i + 4)));
        i += 4;
      }
      return result;
    },
    dinf: function(data) {
      return {
        boxes: inspectMp4(data)
      };
    },
    dref: function(data) {
      return {
        version: data[0],
        flags: new Uint8Array(data.subarray(1, 4)),
        dataReferences: inspectMp4(data.subarray(8))
      };
    },
    hdlr: function(data) {
      var
        view = new DataView(data.buffer, data.byteOffset, data.byteLength),
        language,
        result = {
          version: view.getUint8(0),
          flags: new Uint8Array(data.subarray(1, 4)),
          handlerType: parseType(data.subarray(8, 12)),
          name: ''
        },
        i = 8;

      // parse out the name field
      for (i = 24; i < data.byteLength; i++) {
        if (data[i] === 0x00) {
          // the name field is null-terminated
          i++;
          break;
        }
        result.name += String.fromCharCode(data[i]);
      }
      // decode UTF-8 to javascript's internal representation
      // see http://ecmanaut.blogspot.com/2006/07/encoding-decoding-utf8-in-javascript.html
      result.name = decodeURIComponent(global.escape(result.name));

      return result;
    },
    mdat: function(data) {
      return {
        byteLength: data.byteLength,
        nals: nalParse(data)
      };
    },
    mdhd: function(data) {
      var
        view = new DataView(data.buffer, data.byteOffset, data.byteLength),
        i = 4,
        language,
        result = {
          version: view.getUint8(0),
          flags: new Uint8Array(data.subarray(1, 4)),
          language: ''
        };
      if (result.version === 1) {
        i += 4;
        result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
        i += 8;
        result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
        i += 4;
        result.timescale = view.getUint32(i);
        i += 8;
        result.duration = view.getUint32(i); // truncating top 4 bytes
      } else {
        result.creationTime = parseMp4Date(view.getUint32(i));
        i += 4;
        result.modificationTime = parseMp4Date(view.getUint32(i));
        i += 4;
        result.timescale = view.getUint32(i);
        i += 4;
        result.duration = view.getUint32(i);
      }
      i += 4;
      // language is stored as an ISO-639-2/T code in an array of three 5-bit fields
      // each field is the packed difference between its ASCII value and 0x60
      language = view.getUint16(i);
      result.language += String.fromCharCode((language >> 10) + 0x60);
      result.language += String.fromCharCode(((language & 0x03c0) >> 5) + 0x60);
      result.language += String.fromCharCode((language & 0x1f) + 0x60);

      return result;
    },
    mdia: function(data) {
      return {
        boxes: inspectMp4(data)
      };
    },
    mfhd: function(data) {
      return {
        version: data[0],
        flags: new Uint8Array(data.subarray(1, 4)),
        sequenceNumber: (data[4] << 24) |
          (data[5] << 16) |
          (data[6] << 8) |
          (data[7])
      };
    },
    minf: function(data) {
      return {
        boxes: inspectMp4(data)
      };
    },
    // codingname, not a first-class box type. stsd entries share the
    // same format as real boxes so the parsing infrastructure can be
    // shared
    mp4a: function(data) {
      var
        view = new DataView(data.buffer, data.byteOffset, data.byteLength),
        result = {
          // 6 bytes reserved
          dataReferenceIndex: view.getUint16(6),
          // 4 + 4 bytes reserved
          channelcount: view.getUint16(16),
          samplesize: view.getUint16(18),
          // 2 bytes pre_defined
          // 2 bytes reserved
          samplerate: view.getUint16(24) + (view.getUint16(26) / 65536)
        };

      // if there are more bytes to process, assume this is an ISO/IEC
      // 14496-14 MP4AudioSampleEntry and parse the ESDBox
      if (data.byteLength > 28) {
        result.streamDescriptor = inspectMp4(data.subarray(28))[0];
      }
      return result;
    },
    moof: function(data) {
      return {
        boxes: inspectMp4(data)
      };
    },
    moov: function(data) {
      return {
        boxes: inspectMp4(data)
      };
    },
    mvex: function(data) {
      return {
        boxes: inspectMp4(data)
      };
    },
    mvhd: function(data) {
      var
        view = new DataView(data.buffer, data.byteOffset, data.byteLength),
        i = 4,
        result = {
          version: view.getUint8(0),
          flags: new Uint8Array(data.subarray(1, 4))
        };

      if (result.version === 1) {
        i += 4;
        result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
        i += 8;
        result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
        i += 4;
        result.timescale = view.getUint32(i);
        i += 8;
        result.duration = view.getUint32(i); // truncating top 4 bytes
      } else {
        result.creationTime = parseMp4Date(view.getUint32(i));
        i += 4;
        result.modificationTime = parseMp4Date(view.getUint32(i));
        i += 4;
        result.timescale = view.getUint32(i);
        i += 4;
        result.duration = view.getUint32(i);
      }
      i += 4;

      // convert fixed-point, base 16 back to a number
      result.rate = view.getUint16(i) + (view.getUint16(i + 2) / 16);
      i += 4;
      result.volume = view.getUint8(i) + (view.getUint8(i + 1) / 8);
      i += 2;
      i += 2;
      i += 2 * 4;
      result.matrix = new Uint32Array(data.subarray(i, i + (9 * 4)));
      i += 9 * 4;
      i += 6 * 4;
      result.nextTrackId = view.getUint32(i);
      return result;
    },
    pdin: function(data) {
      var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
      return {
        version: view.getUint8(0),
        flags: new Uint8Array(data.subarray(1, 4)),
        rate: view.getUint32(4),
        initialDelay: view.getUint32(8)
      };
    },
    sdtp: function(data) {
      var
        result = {
          version: data[0],
          flags: new Uint8Array(data.subarray(1, 4)),
          samples: []
        }, i;

      for (i = 4; i < data.byteLength; i++) {
        result.samples.push({
          dependsOn: (data[i] & 0x30) >> 4,
          isDependedOn: (data[i] & 0x0c) >> 2,
          hasRedundancy: data[i] & 0x03
        });
      }
      return result;
    },
    sidx: function(data) {
      var view = new DataView(data.buffer, data.byteOffset, data.byteLength),
          result = {
            version: data[0],
            flags: new Uint8Array(data.subarray(1, 4)),
            references: [],
            referenceId: view.getUint32(4),
            timescale: view.getUint32(8),
            earliestPresentationTime: view.getUint32(12),
            firstOffset: view.getUint32(16)
          },
          referenceCount = view.getUint16(22),
          i;

      for (i = 24; referenceCount; i += 12, referenceCount-- ) {
        result.references.push({
          referenceType: (data[i] & 0x80) >>> 7,
          referencedSize: view.getUint32(i) & 0x7FFFFFFF,
          subsegmentDuration: view.getUint32(i + 4),
          startsWithSap: !!(data[i + 8] & 0x80),
          sapType: (data[i + 8] & 0x70) >>> 4,
          sapDeltaTime: view.getUint32(i + 8) & 0x0FFFFFFF
        });
      }

      return result;
    },
    smhd: function(data) {
      return {
        version: data[0],
        flags: new Uint8Array(data.subarray(1, 4)),
        balance: data[4] + (data[5] / 256)
      };
    },
    stbl: function(data) {
      return {
        boxes: inspectMp4(data)
      };
    },
    stco: function(data) {
      var
        view = new DataView(data.buffer, data.byteOffset, data.byteLength),
        result = {
          version: data[0],
          flags: new Uint8Array(data.subarray(1, 4)),
          chunkOffsets: []
        },
        entryCount = view.getUint32(4),
        i;
      for (i = 8; entryCount; i += 4, entryCount--) {
        result.chunkOffsets.push(view.getUint32(i));
      }
      return result;
    },
    stsc: function(data) {
      var
        view = new DataView(data.buffer, data.byteOffset, data.byteLength),
        entryCount = view.getUint32(4),
        result = {
          version: data[0],
          flags: new Uint8Array(data.subarray(1, 4)),
          sampleToChunks: []
        },
        i;
      for (i = 8; entryCount; i += 12, entryCount--) {
        result.sampleToChunks.push({
          firstChunk: view.getUint32(i),
          samplesPerChunk: view.getUint32(i + 4),
          sampleDescriptionIndex: view.getUint32(i + 8)
        });
      }
      return result;
    },
    stsd: function(data) {
      return {
        version: data[0],
        flags: new Uint8Array(data.subarray(1, 4)),
        sampleDescriptions: inspectMp4(data.subarray(8))
      };
    },
    stsz: function(data) {
      var
        view = new DataView(data.buffer, data.byteOffset, data.byteLength),
        result = {
          version: data[0],
          flags: new Uint8Array(data.subarray(1, 4)),
          sampleSize: view.getUint32(4),
          entries: []
        },
        i;
      for (i = 12; i < data.byteLength; i += 4) {
        result.entries.push(view.getUint32(i));
      }
      return result;
    },
    stts: function(data) {
      var
        view = new DataView(data.buffer, data.byteOffset, data.byteLength),
        result = {
          version: data[0],
          flags: new Uint8Array(data.subarray(1, 4)),
          timeToSamples: []
        },
        entryCount = view.getUint32(4),
        i;

      for (i = 8; entryCount; i += 8, entryCount--) {
        result.timeToSamples.push({
          sampleCount: view.getUint32(i),
          sampleDelta: view.getUint32(i + 4)
        });
      }
      return result;
    },
    styp: function(data) {
      return parse.ftyp(data);
    },
    tfdt: function(data) {
      return {
        version: data[0],
        flags: new Uint8Array(data.subarray(1, 4)),
        baseMediaDecodeTime: data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]
      };
    },
    tfhd: function(data) {
      var
        view = new DataView(data.buffer, data.byteOffset, data.byteLength),
        result = {
          version: data[0],
          flags: new Uint8Array(data.subarray(1, 4)),
          trackId: view.getUint32(4)
        },
        baseDataOffsetPresent = result.flags[2] & 0x01,
        sampleDescriptionIndexPresent = result.flags[2] & 0x02,
        defaultSampleDurationPresent = result.flags[2] & 0x08,
        defaultSampleSizePresent = result.flags[2] & 0x10,
        defaultSampleFlagsPresent = result.flags[2] & 0x20,
        i;

      i = 8;
      if (baseDataOffsetPresent) {
        i += 4; // truncate top 4 bytes
        result.baseDataOffset = view.getUint32(12);
        i += 4;
      }
      if (sampleDescriptionIndexPresent) {
        result.sampleDescriptionIndex = view.getUint32(i);
        i += 4;
      }
      if (defaultSampleDurationPresent) {
        result.defaultSampleDuration = view.getUint32(i);
        i += 4;
      }
      if (defaultSampleSizePresent) {
        result.defaultSampleSize = view.getUint32(i);
        i += 4;
      }
      if (defaultSampleFlagsPresent) {
        result.defaultSampleFlags = view.getUint32(i);
      }
      return result;
    },
    tkhd: function(data) {
      var
        view = new DataView(data.buffer, data.byteOffset, data.byteLength),
        i = 4,
        result = {
          version: view.getUint8(0),
          flags: new Uint8Array(data.subarray(1, 4)),
        };
      if (result.version === 1) {
        i += 4;
        result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
        i += 8;
        result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
        i += 4;
        result.trackId = view.getUint32(i);
        i += 4;
        i += 8;
        result.duration = view.getUint32(i); // truncating top 4 bytes
      } else {
        result.creationTime = parseMp4Date(view.getUint32(i));
        i += 4;
        result.modificationTime = parseMp4Date(view.getUint32(i));
        i += 4;
        result.trackId = view.getUint32(i);
        i += 4;
        i += 4;
        result.duration = view.getUint32(i);
      }
      i += 4;
      i += 2 * 4;
      result.layer = view.getUint16(i);
      i += 2;
      result.alternateGroup = view.getUint16(i);
      i += 2;
      // convert fixed-point, base 16 back to a number
      result.volume = view.getUint8(i) + (view.getUint8(i + 1) / 8);
      i += 2;
      i += 2;
      result.matrix = new Uint32Array(data.subarray(i, i + (9 * 4)));
      i += 9 * 4;
      result.width = view.getUint16(i) + (view.getUint16(i + 2) / 16);
      i += 4;
      result.height = view.getUint16(i) + (view.getUint16(i + 2) / 16);
      return result;
    },
    traf: function(data) {
      return {
        boxes: inspectMp4(data)
      };
    },
    trak: function(data) {
      return {
        boxes: inspectMp4(data)
      };
    },
    trex: function(data) {
      var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
      return {
        version: data[0],
        flags: new Uint8Array(data.subarray(1, 4)),
        trackId: view.getUint32(4),
        defaultSampleDescriptionIndex: view.getUint32(8),
        defaultSampleDuration: view.getUint32(12),
        defaultSampleSize: view.getUint32(16),
        sampleDependsOn: data[20] & 0x03,
        sampleIsDependedOn: (data[21] & 0xc0) >> 6,
        sampleHasRedundancy: (data[21] & 0x30) >> 4,
        samplePaddingValue: (data[21] & 0x0e) >> 1,
        sampleIsDifferenceSample: !!(data[21] & 0x01),
        sampleDegradationPriority: view.getUint16(22)
      };
    },
    trun: function(data) {
      var
        result = {
          version: data[0],
          flags: new Uint8Array(data.subarray(1, 4)),
          samples: []
        },
        view = new DataView(data.buffer, data.byteOffset, data.byteLength),
        dataOffsetPresent = result.flags[2] & 0x01,
        firstSampleFlagsPresent = result.flags[2] & 0x04,
        sampleDurationPresent = result.flags[1] & 0x01,
        sampleSizePresent = result.flags[1] & 0x02,
        sampleFlagsPresent = result.flags[1] & 0x04,
        sampleCompositionTimeOffsetPresent = result.flags[1] & 0x08,
        sampleCount = view.getUint32(4),
        offset = 8,
        sample;

      if (dataOffsetPresent) {
        result.dataOffset = view.getUint32(offset);
        offset += 4;
      }

      if (firstSampleFlagsPresent && sampleCount) {
        sample = {
          flags: parseSampleFlags(data.subarray(offset, offset + 4))
        };
        offset += 4;
        if (sampleDurationPresent) {
          sample.duration = view.getUint32(offset);
          offset += 4;
        }
        if (sampleSizePresent) {
          sample.size = view.getUint32(offset);
          offset += 4;
        }
        if (sampleCompositionTimeOffsetPresent) {
          sample.compositionTimeOffset = view.getUint32(offset);
          offset += 4;
        }
        result.samples.push(sample);
        sampleCount--;
      }

      while (sampleCount--) {
        sample = {};
        if (sampleDurationPresent) {
          sample.duration = view.getUint32(offset);
          offset += 4;
        }
        if (sampleSizePresent) {
          sample.size = view.getUint32(offset);
          offset += 4;
        }
        if (sampleFlagsPresent) {
          sample.flags = parseSampleFlags(data.subarray(offset, offset + 4));
          offset += 4;
        }
        if (sampleCompositionTimeOffsetPresent) {
          sample.compositionTimeOffset = view.getUint32(offset);
          offset += 4;
        }
        result.samples.push(sample);
      }
      return result;
    },
    'url ': function(data) {
      return {
        version: data[0],
        flags: new Uint8Array(data.subarray(1, 4))
      };
    },
    vmhd: function(data) {
      var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
      return {
        version: data[0],
        flags: new Uint8Array(data.subarray(1, 4)),
        graphicsmode: view.getUint16(4),
        opcolor: new Uint16Array([view.getUint16(6),
                                  view.getUint16(8),
                                  view.getUint16(10)])
      };
    }
  };


/**
 * Return a javascript array of box objects parsed from an ISO base
 * media file.
 * @param data {Uint8Array} the binary data of the media to be inspected
 * @return {array} a javascript array of potentially nested box objects
 */
inspectMp4 = function(data) {
  var
    i = 0,
    result = [],
    view,
    size,
    type,
    end,
    box;

  // Convert data from Uint8Array to ArrayBuffer, to follow Dataview API
  var ab = new ArrayBuffer(data.length);
  var v = new Uint8Array(ab);
  for (var z = 0; z < data.length; ++z) {
      v[z] = data[z];
  }
  view = new DataView(ab);


  while (i < data.byteLength) {
    // parse box data
    size = view.getUint32(i);
    type =  parseType(data.subarray(i + 4, i + 8));
    end = size > 1 ? i + size : data.byteLength;

    // parse type-specific data
    box = (parse[type] || function(data) {
      return {
        data: data
      };
    })(data.subarray(i + 8, end));
    box.size = size;
    box.type = type;

    // store this box and move to the next
    result.push(box);
    i = end;
  }
  return result;
};

/**
 * Returns a textual representation of the javascript represtentation
 * of an MP4 file. You can use it as an alternative to
 * JSON.stringify() to compare inspected MP4s.
 * @param inspectedMp4 {array} the parsed array of boxes in an MP4
 * file
 * @param depth {number} (optional) the number of ancestor boxes of
 * the elements of inspectedMp4. Assumed to be zero if unspecified.
 * @return {string} a text representation of the parsed MP4
 */
textifyMp4 = function(inspectedMp4, depth) {
  var indent;
  depth = depth || 0;
  indent = new Array(depth * 2 + 1).join(' ');

  // iterate over all the boxes
  return inspectedMp4.map(function(box, index) {

    // list the box type first at the current indentation level
    return indent + box.type + '\n' +

      // the type is already included and handle child boxes separately
      Object.keys(box).filter(function(key) {
        return key !== 'type' && key !== 'boxes';

      // output all the box properties
      }).map(function(key) {
        var prefix = indent + '  ' + key + ': ',
            value = box[key];

        // print out raw bytes as hexademical
        if (value instanceof Uint8Array || value instanceof Uint32Array) {
          var bytes = Array.prototype.slice.call(new Uint8Array(value.buffer, value.byteOffset, value.byteLength))
              .map(function(byte) {
                return ' ' + ('00' + byte.toString(16)).slice(-2);
              }).join('').match(/.{1,24}/g);
          if (!bytes) {
            return prefix + '<>';
          }
          if (bytes.length === 1) {
            return prefix + '<' + bytes.join('').slice(1) + '>';
          }
          return prefix + '<\n' + bytes.map(function(line) {
            return indent + '  ' + line;
          }).join('\n') + '\n' + indent + '  >';
        }

        // stringify generic objects
        return prefix +
            JSON.stringify(value, null, 2)
              .split('\n').map(function(line, index) {
                if (index === 0) {
                  return line;
                }
                return indent + '  ' + line;
              }).join('\n');
      }).join('\n') +

    // recursively textify the child boxes
    (box.boxes ? '\n' + textifyMp4(box.boxes, depth + 1) : '');
  }).join('\n');
};

module.exports = {
  inspect: inspectMp4,
  textify: textifyMp4
};

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{}],40:[function(_dereq_,module,exports){
'use strict';

var ExpGolomb;

/**
 * Parser for exponential Golomb codes, a variable-bitwidth number encoding
 * scheme used by h264.
 */
ExpGolomb = function(workingData) {
  var
    // the number of bytes left to examine in workingData
    workingBytesAvailable = workingData.byteLength,

    // the current word being examined
    workingWord = 0, // :uint

    // the number of bits left to examine in the current word
    workingBitsAvailable = 0; // :uint;

  // ():uint
  this.length = function() {
    return (8 * workingBytesAvailable);
  };

  // ():uint
  this.bitsAvailable = function() {
    return (8 * workingBytesAvailable) + workingBitsAvailable;
  };

  // ():void
  this.loadWord = function() {
    var
      position = workingData.byteLength - workingBytesAvailable,
      workingBytes = new Uint8Array(4),
      availableBytes = Math.min(4, workingBytesAvailable);

    if (availableBytes === 0) {
      throw new Error('no bytes available');
    }

    workingBytes.set(workingData.subarray(position,
                                          position + availableBytes));
    workingWord = new DataView(workingBytes.buffer).getUint32(0);

    // track the amount of workingData that has been processed
    workingBitsAvailable = availableBytes * 8;
    workingBytesAvailable -= availableBytes;
  };

  // (count:int):void
  this.skipBits = function(count) {
    var skipBytes; // :int
    if (workingBitsAvailable > count) {
      workingWord          <<= count;
      workingBitsAvailable -= count;
    } else {
      count -= workingBitsAvailable;
      skipBytes = Math.floor(count / 8);

      count -= (skipBytes * 8);
      workingBytesAvailable -= skipBytes;

      this.loadWord();

      workingWord <<= count;
      workingBitsAvailable -= count;
    }
  };

  // (size:int):uint
  this.readBits = function(size) {
    var
      bits = Math.min(workingBitsAvailable, size), // :uint
      valu = workingWord >>> (32 - bits); // :uint
    // if size > 31, handle error
    workingBitsAvailable -= bits;
    if (workingBitsAvailable > 0) {
      workingWord <<= bits;
    } else if (workingBytesAvailable > 0) {
      this.loadWord();
    }

    bits = size - bits;
    return bits > 0 && workingBitsAvailable ? valu << bits | this.readBits(bits) : valu;
  };

  // ():uint
  this.skipLeadingZeros = function() {
    var leadingZeroCount; // :uint
    for (leadingZeroCount = 0 ; leadingZeroCount < workingBitsAvailable ; ++leadingZeroCount) {
      if (0 !== (workingWord & (0x80000000 >>> leadingZeroCount))) {
        // the first bit of working word is 1
        workingWord <<= leadingZeroCount;
        workingBitsAvailable -= leadingZeroCount;
        return leadingZeroCount;
      }
    }

    // we exhausted workingWord and still have not found a 1
    this.loadWord();
    return leadingZeroCount + this.skipLeadingZeros();
  };

  // ():void
  this.skipUnsignedExpGolomb = function() {
    this.skipBits(1 + this.skipLeadingZeros());
  };

  // ():void
  this.skipExpGolomb = function() {
    this.skipBits(1 + this.skipLeadingZeros());
  };

  // ():uint
  this.readUnsignedExpGolomb = function() {
    var clz = this.skipLeadingZeros(); // :uint
    return this.readBits(clz + 1) - 1;
  };

  // ():int
  this.readExpGolomb = function() {
    var valu = this.readUnsignedExpGolomb(); // :int
    return valu & 0x01 ? (1 + valu) >>> 1 : -(valu >>> 1);
  };

  // Some convenience functions
  // :Boolean
  this.readBoolean = function() {
    return 1 === this.readBits(1);
  };

  // ():int
  this.readUnsignedByte = function() {
    return this.readBits(8);
  };

  this.loadWord();
};

module.exports = ExpGolomb;

},{}],41:[function(_dereq_,module,exports){
/**
 * mux.js
 *
 * Copyright (c) 2014 Brightcove
 * All rights reserved.
 *
 * A lightweight readable stream implemention that handles event dispatching.
 * Objects that inherit from streams should call init in their constructors.
 */
'use strict';

var Stream = function() {
  this.init = function() {
    var listeners = {};
    /**
     * Add a listener for a specified event type.
     * @param type {string} the event name
     * @param listener {function} the callback to be invoked when an event of
     * the specified type occurs
     */
    this.on = function(type, listener) {
      if (!listeners[type]) {
        listeners[type] = [];
      }
      listeners[type].push(listener);
    };
    /**
     * Remove a listener for a specified event type.
     * @param type {string} the event name
     * @param listener {function} a function previously registered for this
     * type of event through `on`
     */
    this.off = function(type, listener) {
      var index;
      if (!listeners[type]) {
        return false;
      }
      index = listeners[type].indexOf(listener);
      listeners[type].splice(index, 1);
      return index > -1;
    };
    /**
     * Trigger an event of the specified type on this stream. Any additional
     * arguments to this function are passed as parameters to event listeners.
     * @param type {string} the event name
     */
    this.trigger = function(type) {
      var callbacks, i, length, args;
      callbacks = listeners[type];
      if (!callbacks) {
        return;
      }
      // Copy handlers so if handlers are added/removed during the process it
      // doesn't throw everything off.
      callbacks = callbacks.slice(0);
      // Slicing the arguments on every invocation of this method
      // can add a significant amount of overhead. Avoid the
      // intermediate object creation for the common case of a
      // single callback argument
      if (arguments.length === 2) {
        length = callbacks.length;
        for (i = 0; i < length; ++i) {
          callbacks[i].call(this, arguments[1]);
        }
      } else {
        args = [];
        i = arguments.length;
        for (i = 1; i < arguments.length; ++i) {
          args.push(arguments[i]);
        }
        length = callbacks.length;
        for (i = 0; i < length; ++i) {
          callbacks[i].apply(this, args);
        }
      }
    };
    /**
     * Destroys the stream and cleans up.
     */
    this.dispose = function() {
      listeners = {};
    };
  };
};

/**
 * Forwards all `data` events on this stream to the destination stream. The
 * destination stream should provide a method `push` to receive the data
 * events as they arrive.
 * @param destination {stream} the stream that will receive all `data` events
 * @param autoFlush {boolean} if false, we will not call `flush` on the destination
 *                            when the current stream emits a 'done' event
 * @see http://nodejs.org/api/stream.html#stream_readable_pipe_destination_options
 */
Stream.prototype.pipe = function(destination) {
  this.on('data', function(data) {
    destination.push(data);
  });

  this.on('done', function(flushSource) {
    destination.flush(flushSource);
  });

  return destination;
};

// Default stream functions that are expected to be overridden to perform
// actual work. These are provided by the prototype as a sort of no-op
// implementation so that we don't have to check for their existence in the
// `pipe` function above.
Stream.prototype.push = function(data) {
  this.trigger('data', data);
};

Stream.prototype.flush = function(flushSource) {
  this.trigger('done', flushSource);
};

module.exports = Stream;

},{}],42:[function(_dereq_,module,exports){
var bundleFn = arguments[3];
var sources = arguments[4];
var cache = arguments[5];

var stringify = JSON.stringify;

module.exports = function (fn) {
    var keys = [];
    var wkey;
    var cacheKeys = Object.keys(cache);
    
    for (var i = 0, l = cacheKeys.length; i < l; i++) {
        var key = cacheKeys[i];
        if (cache[key].exports === fn) {
            wkey = key;
            break;
        }
    }
    
    if (!wkey) {
        wkey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
        var wcache = {};
        for (var i = 0, l = cacheKeys.length; i < l; i++) {
            var key = cacheKeys[i];
            wcache[key] = key;
        }
        sources[wkey] = [
            Function(['require','module','exports'], '(' + fn + ')(self)'),
            wcache
        ];
    }
    var skey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
    
    var scache = {}; scache[wkey] = wkey;
    sources[skey] = [
        Function(['require'],'require(' + stringify(wkey) + ')(self)'),
        scache
    ];
    
    var src = '(' + bundleFn + ')({'
        + Object.keys(sources).map(function (key) {
            return stringify(key) + ':['
                + sources[key][0]
                + ',' + stringify(sources[key][1]) + ']'
            ;
        }).join(',')
        + '},{},[' + stringify(skey) + '])'
    ;
    
    var URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
    
    return new Worker(URL.createObjectURL(
        new Blob([src], { type: 'text/javascript' })
    ));
};

},{}],43:[function(_dereq_,module,exports){
(function(window, vjs){
'use strict';
var volume_icon_svg = '<svg height="100%" width="100%" viewBox="0 0 36 36">'
    +'<defs><clipPath id="{vjs-volume-mask1}">'
    +'<path d="m 14.35,-0.14 -5.86,5.86 20.73,20.78 5.86,-5.91 z"></path>'
    +'<path d="M 7.07,6.87 -1.11,15.33 19.61,36.11 27.80,27.60 z"></path>'
    +'<path class="vjs-volume-mask" d="M 9.09,5.20 6.47,7.88 26.82,28.77 '
    +'29.66,25.99 z"></path></clipPath><clipPath id="{vjs-volume-mask2}">'
    +'<path class="vjs-volume-mask" d="m -11.45,-15.55 -4.44,4.51 20.45,20.94 '
    +'4.55,-4.66 z"></path></clipPath></defs>'
    +'<g clip-path="url(#{vjs-volume-mask1})"><path d="M8,21 L12,21 L17,26 '
    +'L17,10 L12,15 L8,15 L8,21 Z M19,14 L19,22 C20.48,21.32 21.5,19.77 '
    +'21.5,18 C21.5,16.26 20.48,14.74 19,14 Z"></path>'
    +'<path class="volume-level-2" d="M19,11.29 C21.89,12.15 24,'
    +'14.83 24,18 C24,21.17 21.89,23.85 19,24.71 L19,26.77 C23.01,25.86 '
    +'26,22.28 26,18 C26,13.72 23.01,10.14 19,9.23 L19,11.29 Z"></path></g>'
    +'<path clip-path="url(#{vjs-volume-mask2})" d="M 9.25,9 7.98,10.27 '
    +'24.71,27 l 1.27,-1.27 Z"></path>'
    +'</svg>';
var fullscreen_svg = '<svg height="100%" width="100%" viewBox="0 0 36 36">'
    +'<g><path d="m 10,16 2,0 0,-4 4,0 0,-2 L 10,10 l 0,6 0,0 z"/></g>'
    +'<g><path d="m 20,10 0,2 4,0 0,4 2,0 L 26,10 l -6,0 0,0 z"/></g>'
    +'<g><path d="m 24,24 -4,0 0,2 L 26,26 l 0,-6 -2,0 0,4 0,0 z"/></g>'
    +'<g><path d="M 12,20 10,20 10,26 l 6,0 0,-2 -4,0 0,-4 0,0 z"/></g>'
    +'</svg>';
var exit_fullscreen_svg = '<svg height="100%" width="100%" viewBox="0 0 36 36">'
    +'<g><path d="m 20,26 2,0 0,-4 4,0 0,-2 -6,0 0,6 0,0 z"/></g>'
    +'<g><path d="m 10,22 4,0 0,4 2,0 0,-6 -6,0 0,2 0,0 z"/></g>'
    +'<g><path d="m 14,14 -4,0 0,2 6,0 0,-6 -2,0 0,4 0,0 z"/></g>'
    +'<g><path d="m 22,14 0,-4 -2,0 0,6 6,0 0,-2 -4,0 0,0 z"/></g>'
    +'</svg>';
var play_button_svg = '<svg height="100%" width="100%" viewBox="0 14 96 68">'
    +'<path d="M96,44.3v7.3c-0.1,7.7-1,15.5-1,15.5s-0.9,6.6-3.8,9.5c-3.6,3.8-'
    +'7.7,3.8-9.6,4c-13.4,1-33.5,0.9-33.5,0.9 c-0.8,0-25-0.2-32.5-0.9c-2.1-0.4'
    +'-6.9-0.3-10.6-4.1c-2.9-2.9-3.8-9.5-3.8-9.5s-1-7.7-1.1-15.5v-7.3c0.2-7.8,'
    +'1.1-15.5,1.1-15.5 s0.9-6.6,3.8-9.5c3.6-3.8,7.7-3.8,9.6-4.1c13.4-1,33.5-'
    +'0.9,33.5-0.9s20.1-0.1,33.5,0.9c1.9,0.2,5.9,0.2,9.6,4.1 c2.9,2.9,3.8,9.5,'
    +'3.8,9.5S95.9,36.6,96,44.3z M38.3,61.4L64,47.9L38.3,34.4V61.4z"/>'
    +'<polygon points="64,47.9 38.3,61.4 38.3,34.4" fill="#fff"/>'
    +'</svg>';
var slider_gaps = '<div class="vjs-slider-gap-left"></div>'
    +'<div class="vjs-slider-gap-right"></div>';
var skin = '.vjs5-hola-skin';
var custom_css = skin+' .vjs-big-play-button:hover .vjs-button-icon {'
    +'    fill: @play_button_color;'
    +'}'
    +skin+':not(.vjs-ad-playing) .vjs-progress-control .vjs-play-progress,'
    +skin+' .vjs-progress-control .vjs-mouse-display:before,'
    +skin+' .vjs-progress-control .vjs-play-progress:before,'
    +skin+' .vjs-captions-toggle>.vjs-button-icon:after {'
    +'    background-color: @seek_bar_color;'
    +'}';

function add_css(url, ver){
    var link = document.createElement('link');
    link.setAttribute('rel', 'stylesheet');
    link.setAttribute('href', url+(ver ? '?'+ver : ''));
    document.getElementsByTagName('head')[0].appendChild(link);
}

var Component = vjs.getComponent('Component');
var ControlBar = vjs.getComponent('ControlBar');
var Button = vjs.getComponent('Button');
var MenuButton = vjs.getComponent('MenuButton');
var VolumeMenuButton = vjs.getComponent('VolumeMenuButton');
var FullscreenToggle = vjs.getComponent('FullscreenToggle');
var BigPlayButton = vjs.getComponent('BigPlayButton');
var SeekBar = vjs.getComponent('SeekBar');
var LoadingSpinner = vjs.getComponent('LoadingSpinner');
var Tooltip = vjs.getComponent('Tooltip');
var patched = false;

function patch_controls_default(){
    if (patched)
        return;
    patched = true;
    var controlbar_createEl = ControlBar.prototype.createEl;
    ControlBar.prototype.createEl = function(){
        var el = controlbar_createEl.call(this);
        el.appendChild(vjs.createEl('div',
            {className: 'vjs-gradient vjs-bottom-gradient'}));
        return el;
    };
    VolumeMenuButton.prototype.createEl = function(){
        var el = MenuButton.prototype.createEl.call(this);
        var id = this.player_.id();
        var svg = volume_icon_svg
            .replace(/{vjs-volume-mask1}/g, 'vjs-volume-mask1_'+id)
            .replace(/{vjs-volume-mask2}/g, 'vjs-volume-mask2_'+id);
        var icon = this.icon_ = vjs.createEl('div',
            {className: 'vjs-button-icon', innerHTML: svg});
        el.insertBefore(icon, el.firstChild);
        return el;
    };
    VolumeMenuButton.prototype.tooltipHandler = function(){
        return this.icon_;
    };
    FullscreenToggle.prototype.controlText_ = 'Full screen';
    FullscreenToggle.prototype.createEl = function(){
        var el = Button.prototype.createEl.call(this);
        el.insertBefore(vjs.createEl('div', {
            className: 'vjs-button-icon vjs-fullscreen-icon',
            innerHTML: fullscreen_svg,
        }), el.firstChild);
        el.insertBefore(vjs.createEl('div', {
            className: 'vjs-button-icon vjs-exit-fullscreen-icon',
            innerHTML: exit_fullscreen_svg,
        }), el.firstChild);
        return el;
    };
    FullscreenToggle.prototype.updateHint = function(){
        this.controlText(this.player_.isFullscreen() ? 'Exit full screen' :
            'Full screen');
    };
    BigPlayButton.prototype.createEl = function(){
        var el = Button.prototype.createEl.call(this);
        el.appendChild(vjs.createEl('div', {
            className: 'vjs-button-icon',
            innerHTML: play_button_svg,
        }));
        return el;
    };
    var orig_createEl = SeekBar.prototype.createEl;
    SeekBar.prototype.createEl = function(){
        var el = orig_createEl.call(this);
        el.appendChild(vjs.createEl('div', {className: 'vjs-slider-padding'}));
        return el;
    };
    var spinner_createEl = LoadingSpinner.prototype.createEl;
    LoadingSpinner.prototype.createEl = function(){
        var el = spinner_createEl.call(this);
        var rotator = vjs.createEl('div', {className: 'vjs-spinner-rotator'});
        rotator.appendChild(vjs.createEl('div', {
            className: 'vjs-spinner-left'}));
        rotator.appendChild(vjs.createEl('div', {
            className: 'vjs-spinner-right'}));
        el.appendChild(rotator);
        return el;
    };
    var tooltip_show = Tooltip.prototype.show;
    Tooltip.prototype.show = function(){
        if (this.timeout)
        {
            this.clearTimeout(this.timeout);
            this.timeout = 0;
        }
        return tooltip_show.apply(this, arguments);
    };
}

function get_ui_zoom(player){
    function clamp(zoom){
        // XXX alexeym: disable zoom < 1 because of wrong handling
        // for some elements
        return Math.max(1, zoom);
    }
    var scale = 1;
    if (player && !player.hasClass('vjs-ios-skin'))
        return player.ui_zoom = scale;
    var viewport = window.visualViewport;
    if (viewport&&viewport.scale)
        return player.ui_zoom = clamp(1/viewport.scale);
    var screen = window.screen;
    if (!screen)
        return player.ui_zoom = scale;
    var width_available = need_invert() ? screen.availHeight :
        screen.availWidth;
    if (width_available)
        scale = window.innerWidth/width_available;
    return player.ui_zoom = clamp(scale);
}

function patch_controls_ios(opt){
    if (patched)
        return;
    patched = true;
    var prefix = 'vjs-ios-';
    function init_control(player, el, icon, need_box){
        var bg = vjs.createEl('div', {className: prefix+'background-tint'});
        bg.appendChild(vjs.createEl('div', {className: prefix+'blur'}));
        bg.appendChild(vjs.createEl('div', {className: prefix+'tint'}));
        el.appendChild(bg);
        if (icon)
        {
            el.appendChild(vjs.createEl('div', {className: 'vjs-button-icon',
                innerHTML: typeof icon=='string' ? icon : ''}));
        }
        if (need_box!==false)
            el.className += ' vjs-ios-control-box';
        el.style.zoom = get_ui_zoom(player);
    }
    var controls = ControlBar.prototype.options_.children;
    var play_index = controls.indexOf('playToggle');
    if (!opt.hide_skip_buttons && play_index!=-1)
    {
        controls.splice(play_index+1, 0, 'skipForward');
        controls.splice(play_index, 0, 'skipBackward');
    }
    var controls_create_el = ControlBar.prototype.createEl;
    ControlBar.prototype.createEl = function(){
        var el = controls_create_el.call(this);
        init_control(this.player_, el, null, false);
        return el;
    };
    var init_children = ControlBar.prototype.initChildren;
    ControlBar.prototype.initChildren = function(){
        var res = init_children.call(this);
        var _this = this;
        ['volumeMenuButton', 'fullscreenToggle'].forEach(function(name){
            var control = _this[name];
            if (control&&control.el_)
                _this.player_.el_.appendChild(control.el_);
        });
        return res;
    };
    var volume_create_el = VolumeMenuButton.prototype.createEl;
    VolumeMenuButton.prototype.createEl = function(){
        var el = volume_create_el.call(this);
        init_control(this.player_, el, true);
        return el;
    };
    var fullscreen_create_el = FullscreenToggle.prototype.createEl;
    FullscreenToggle.prototype.createEl = function(){
        var el = fullscreen_create_el.call(this);
        init_control(this.player_, el, true);
        return el;
    };
    var bigplaybutton_create_el = BigPlayButton.prototype.createEl;
    BigPlayButton.prototype.createEl = function(){
        var el = bigplaybutton_create_el.call(this);
        init_control(this.player_, el, true, false);
        return el;
    };
    var seekbar_calculate = SeekBar.prototype.calculateDistance;
    SeekBar.prototype.calculateDistance = function(event){
        var zoom = this.player_.ui_zoom;
        var fake_event = {};
        fake_event.pageY = event.pageY / zoom;
        fake_event.pageX = event.pageX / zoom;
        if (event.changedTouches&&event.changedTouches[0])
        {
            var touch = {};
            touch.pageX = event.changedTouches[0].pageX / zoom;
            touch.pageY = event.changedTouches[0].pageY / zoom;
            fake_event.changedTouches = [touch];
        }
        return seekbar_calculate.call(this, fake_event);
    };
    // hack to improve seeking on Android
    // handle touch events from progress-control component
    var seekbar_mousedown = SeekBar.prototype.handleMouseDown;
    SeekBar.prototype.handleMouseDown = function(event){
        if (this.mouse_down_pressed)
            return;
        this.mouse_down_pressed = true;
        seekbar_mousedown.call(this, event);
    };
    var seekbar_mouseup = SeekBar.prototype.handleMouseUp;
    SeekBar.prototype.handleMouseUp = function(event){
        if (!this.mouse_down_pressed)
            return;
        this.mouse_down_pressed = false;
        seekbar_mouseup.call(this, event);
    };
    SeekBar.prototype.handleMouseMove = function(event){
        if (!this.mouse_down_pressed)
            return;
        var duration = this.player_.duration();
        var distance = this.calculateDistance(event);
        this.scrubbing_distance = distance;
        if (duration)
        {
            this.player_.scrubbing_percent = 0;
            var newTime = distance*duration;
            if (newTime===duration)
                newTime = newTime - 0.1;
            this.player_.currentTime(newTime);
        }
        else
            this.player_.scrubbing_percent = distance;
        if (this.update)
            this.update();
        // Immediate update for the time labels
        // disabled because native iOS player does not do this
        if (0)
        {
        this.player_.controlBar.currentTimeDisplay.updateContent();
        this.player_.controlBar.remainingTimeDisplay.updateContent();
        }
    };
    SeekBar.prototype.getPercent = function(){
        var duration = this.player_.duration();
        var percent;
        if (duration || !this.player_.scrubbing_percent)
        {
            percent = this.player_.scrubbing() ?
                this.scrubbing_distance :
                this.player_.currentTime() / duration;
        }
        else
            percent = this.player_.scrubbing_percent;
        return percent >= 1 ? 1 : percent;
    };
}

var HolaSkin = function(player, opt){
    var _this = this;
    this.player = player;
    this.el = player.el();
    this.opt = opt;
    this.classes_added = [];
    var ios_skin = opt.className=='vjs-ios-skin';
    // values are found empirically from the native iOS player
    this.controls_min_width = ios_skin ?
        [{name: 'skipBackward', min_width: 306},
        {name: 'skipForward', min_width: 335},
        {name: 'currentTimeDisplay', min_width: 260}] : [];
    this.external_controls = ios_skin ?
        ['volumeMenuButton', 'fullscreenToggle'] : [];
    player.on('dispose', function(){ _this.dispose(); });
    player.on('ready', function(){ _this.init(); });
    var resize = this._resize = this.resize.bind(this);
    var zoom_end = this._zoom_end = this.on_touch_end.bind(this);
    player.on('resize', resize);
    player.on('fullscreenchange', function(){ setTimeout(resize); });
    window.addEventListener('resize', resize);
    window.addEventListener('orientationchange', resize);
    this.scale = 1;
    document.addEventListener('touchend', zoom_end);
    this.apply();
};

HolaSkin.prototype.apply = function(){
    var c, classes = [this.opt.className];
    if (this.opt.show_controls_before_start)
        classes.push('vjs-show-controls-before-start');
    if (this.opt.show_time_for_live)
        classes.push('vjs-show-time-for-live');
    while (c = classes.shift())
    {
        if (this.player.addClass(c))
            this.classes_added.push(c);
    }
};

function need_invert(){
    var screen = window.screen;
    if (!screen)
        return false;
    // ios safari always use values for screen.avail*
    // from vertical rotation while android chrome change it
    // depending on the orientation
    var s = screen.availHeight > screen.availWidth;
    var w = window.innerHeight > window.innerWidth;
    return s!=w;
}

HolaSkin.prototype.update_scrubbing = function(){
    if (!this.player.scrubbing_percent)
        return;
    var duration = this.player.duration();
    if (duration===Infinity)
    {
        this.player.scrubbing_percent = 0;
        return;
    }
    if (!duration||this.player.hasClass('vjs-waiting'))
        return;
    var newTime = this.player.scrubbing_percent*duration;
    if (newTime===duration)
        newTime = newTime - 0.1;
    this.player.scrubbing_percent = 0;
    this.player.currentTime(newTime);
};

HolaSkin.prototype.on_touch_end = function(e){
    var scale = get_ui_zoom(this.player);
    if (this._zoom_bounce)
        clearTimeout(this._zoom_bounce);
    if (this.scale==scale)
    {
        // XXX alexeym: hack for animation when zoom via double-tap
        if (e)
            this._zoom_bounce = setTimeout(this._zoom_end, 500);
        return;
    }
    var zooming_out = this.scale<scale;
    this.scale = scale;
    this._resize();
    // XXX alexeym: hack for bounce-on-zoom animation when zooming out
    if (zooming_out)
        this._zoom_bounce = setTimeout(this._resize, 500);
};

HolaSkin.prototype.resize = function(){
    var player = this.player;
    var width = this.el.offsetWidth;
    player.toggleClass('vjs-small', width<=480);
    function get_child(name){
        var control_bar = player.controlBar;
        return player[name] || control_bar&&control_bar[name] ||
            player.getChild(name);
    }
    var zoom = get_ui_zoom(player);
    ['controlBar', 'bigPlayButton', 'ShareButton']
    .concat(this.external_controls).forEach(function(name){
        var control = get_child(name);
        if (!control)
            return;
        var el = control.el();
        if (!el)
            return;
        el.style.zoom = zoom;
    });
    var s_width = width/zoom;
    this.controls_min_width.forEach(function(item){
        var control = get_child(item.name);
        if (!control)
            return;
        control.toggleClass('vjs-hidden', s_width<item.min_width);
    });
    // XXX alexeym TODO: show only play/pause button if s_width<201
};

HolaSkin.prototype.init = function(){
    var _this = this;
    var player = this.player;
    this.resize();
    if (vjs.browser.IS_ANDROID || vjs.browser.IS_IOS)
        player.addClass('vjs-mobile');
    this.init_volume_button();
    this.has_played = false;
    var play_el = player.controlBar.playToggle.el();
    play_el.appendChild(vjs.createEl('div', {className: 'vjs-button-icon'}));
    var update_scrubbing = this.update_scrubbing.bind(this);
    player.on('play', function(){
        _this.is_ended = false;
        _this.update_state(player);
    })
    .on('ended', function(){
        _this.is_ended = true;
        _this.update_state(player);
    })
    .on('seeking', function(){
        // hide replay button if it's not rewind to the start (cur time == 0)
        if (player.currentTime())
        {
            _this.is_ended = false;
            _this.has_played = true;
        }
        else
            _this.has_played = false;
        _this.update_state(player);
    })
    .on('timeupdate', function(val, t1, t2){
        var has_pos = !!player.currentTime();
        if (has_pos==_this.has_played)
            return;
        update_scrubbing();
        _this.has_played = has_pos;
        _this.update_state(player);
    })
    .on('mouseleave', function(){
        if (!player.hasClass('vjs-settings-expanded'))
            player.userActive(false);
    })
    .one('loadmetadata', update_scrubbing)
    .one('canplay', update_scrubbing)
    .one('canplaythrough', update_scrubbing)
    .one('playing', update_scrubbing);
    this.update_state(player);
    var control_bar = player.controlBar;
    var progress_control = control_bar.progressControl;
    var seek_bar = progress_control.seekBar;
    var progress_holder = seek_bar.el();
    progress_control.on('touchstart', function(e){
        progress_control.addClass('vjs-touch');
        // hack to improve seeking on Android
        // handle touch events from progress-control component
        seek_bar.handleMouseDown(e);
    });
    progress_control.on('touchmove', function(e){
        seek_bar.handleMouseMove(e);
    });
    progress_control.on('touchend', function(e){
        seek_bar.handleMouseUp(e);
        progress_control.removeClass('vjs-touch');
    });
    progress_holder.insertAdjacentHTML('beforeend', slider_gaps);
    player.addChild('PlayAnimation');
    var volume_btn = control_bar.volumeMenuButton;
    var volume_slider = volume_btn.volumeBar.el();
    volume_slider.insertAdjacentHTML('beforeend', slider_gaps);
    volume_btn.on('mouseenter', function(){
        volume_btn.addClass('vjs-show-volume-button'); });
    var interval, report_activity = player.reportUserActivity.bind(player);
    control_bar.on('mouseenter', function(){
        interval = this.setInterval(report_activity, 250); });
    control_bar.on('mouseleave', function(){
        if (interval)
            this.clearInterval(interval);
        volume_btn.removeClass('vjs-show-volume-button');
    });
    var spacer = control_bar.customControlSpacer;
    var spacer_el = spacer&&spacer.el();
    if (spacer_el)
    {
        control_bar.on('mousemove', function(e){
            var r = spacer_el.getBoundingClientRect();
            if (e.clientX>r.left || e.clientY<r.top)
                volume_btn.removeClass('vjs-show-volume-button');
        });
    }
    if (this.opt.title)
        player.addChild('TopBar', {title: this.opt.title});
};

HolaSkin.prototype.update_state = function(player){
    var replay_classname = 'vjs-play-control-replay';
    player.controlBar.playToggle.toggleClass(replay_classname, this.is_ended);
    player.bigPlayButton.toggleClass(replay_classname, this.is_ended);
    player.toggleClass('vjs-pos-ended', this.is_ended && this.has_played);
    player.toggleClass('vjs-pos-started', this.has_played);
    var poster = player.posterImage.el();
    if (this.is_ended)
    {
        player.controlBar.playToggle.controlText('Replay');
        setTimeout(function(){ poster.style.opacity = 1; });
    }
    else
        poster.style.opacity = '';
};

HolaSkin.prototype.init_volume_button = function(){
    var player = this.player, btn = player.controlBar.volumeMenuButton;
    var override = function(obj, events, method, fn){
        var p = VolumeMenuButton.prototype;
        btn.off(obj, events, p[method]);
        btn.on(obj, events, fn);
        btn[method] = fn;
    };
    override(player, ['volumechange', 'advolumechange', 'loadstart'],
        'volumeUpdate', function()
    {
        var i, el = this.el_, _this = this;
        var vol = !this.player_.muted() && this.player_.volume();
        var level = !vol ? 0 : vol<0.5 ? 1 : 2;
        for (i = 0; i < 2; i++)
            vjs.toggleClass(el, 'vjs-vol-'+i, i==level);
        var muted = !vol;
        if (this.muted_!==undefined && this.muted_==muted)
            return;
        this.muted_ = muted;
        this.controlText(muted ? 'Unmute' : 'Mute');
        var masks = el.querySelectorAll('.vjs-volume-mask');
        var from = vol ? 20 : 0, to = vol ? 0 : 20;
        var start = window.performance && window.performance.now();
        var animate = function(time){
            var progress = time ? (time-start)/250 : 1;
            var v = from+(to-from)*Math.max(Math.min(progress, 1), 0);
            for (i = 0; i < masks.length; i++)
                masks[i].setAttribute('transform', 'translate('+v+','+v+')');
            if (v!=to)
                _this.animId = window.requestAnimationFrame(animate);
        };
        if (!start || !window.requestAnimationFrame)
            return void animate();
        if (this.animId)
            window.cancelAnimationFrame(this.animId);
        this.animId = window.requestAnimationFrame(animate);
    });
    var prevent_click = false;
    override(btn, ['tap', 'click'], 'handleClick', function(){
        if (prevent_click)
            return;
        this.player_.muted(!this.player_.muted() && !!this.player_.volume());
        if (!this.player_.muted() && !this.player_.volume())
            this.player_.volume(1);
    });
    btn.on('mousedown', function(event){
        prevent_click = this.volumeBar.el().contains(event.target);
    });
    btn.volumeUpdate();
};

HolaSkin.prototype.dispose = function(){
    while (this.classes_added.length)
        this.player.removeClass(this.classes_added.pop());
    window.removeEventListener('resize', this._resize);
    window.removeEventListener('orientationchange', this._resize);
    document.removeEventListener('touchend', this._zoom_end);
    if (this._zoom_bounce)
        this._zoom_bounce = clearTimeout(this._zoom_bounce);
};

vjs.registerComponent('ControlsWatermark', vjs.extend(Button, {
    constructor: function(player, opt){
        Button.apply(this, arguments);
        if (opt.tooltip)
            this.controlText(opt.tooltip);
    },
    createEl: function(){
        var opt = this.options_;
        var el = vjs.createEl('div', {className:
            'vjs-controls-watermark vjs-control'});
        if (!opt.image)
            return el;
        var container = opt.url ? vjs.createEl('a', {href: opt.url,
            target: '_blank'}) : el;
        if (opt.url)
            el.insertBefore(container, el.firstChild);
        container.appendChild(vjs.createEl('img', {src: opt.image}));
        return el;
    },
    handleClick: function(){
        if (this.options_.url)
            this.player_.pause();
    },
    buildCSSClass: function(){
        return 'vjs-controls-watermark '+
            Button.prototype.buildCSSClass.apply(this, arguments);
    }
}));

vjs.registerComponent('PlayAnimation', vjs.extend(Component, {
    constructor: function(player, opt){
        Component.apply(this, arguments);
        var _this = this, timeout;
        player.on(['videoclick'], function(){
            if (!player.hasStarted())
                return;
            _this.el_.style.display = 'block';
            _this.clearTimeout(timeout);
            timeout = _this.setTimeout(function(){
                _this.el_.style.display = '';
            }, 500);
        });
    },
    createEl: function(){
        var el = Component.prototype.createEl.apply(this, arguments);
        el.className = 'vjs-play-animation';
        return el;
    },
}));

vjs.registerComponent('TopBar', vjs.extend(Component, {
    createEl: function(){
        var el = vjs.createEl('div', {className: 'vjs-top-bar'});
        el.appendChild(vjs.createEl('div',
            {className: 'vjs-gradient vjs-top-gradient'}));
        el.appendChild(vjs.createEl('div', {className: 'vjs-video-title',
            innerHTML: this.options_.title}));
        return el;
    },
}));

vjs.registerComponent('SkipForward', vjs.extend(Button, {
    skip: 15,
    className: 'vjs-skip-forward',
    createEl: function(){
        var el = Button.prototype.createEl.call(this);
        var icon = vjs.createEl('div',
            {className: 'vjs-button-icon', innerHTML: ''});
        el.insertBefore(icon, el.firstChild);
        return el;
    },
    buildCSSClass: function(){
        var parent_class = Button.prototype.buildCSSClass.call(this);
        return this.className+' vjs-skip-button '+parent_class;
    },
    handleClick: function(){
        var current = this.player_.currentTime();
        this.player_.currentTime(current+this.skip);
    },
}));
var SkipForward = vjs.getComponent('SkipForward');
vjs.registerComponent('SkipBackward', vjs.extend(SkipForward, {skip: -15,
    className: 'vjs-skip-back'}));

var defaults = {
    className: 'vjs5-hola-skin',
    css: '/css/videojs-hola-skin.css',
    ver: 'ver={[version]}'
};

vjs.plugin('hola_skin', function(options){
    if (options === false)
        options = {css: false, className: false};
    var opt = vjs.mergeOptions(defaults, options);
    if (opt.css && (!options.className || options.css))
        add_css(opt.css, opt.ver);
    add_css('https://fonts.googleapis.com/css?family=Roboto:400,500');
    if (opt.play_button_color||opt.seek_bar_color)
    {
        custom_css = custom_css
            .replace(/@play_button_color/g, opt.play_button_color||'#00b7f1')
            .replace(/@seek_bar_color/g, opt.seek_bar_color||'#00b7f1');
        _dereq_('browserify-css').createStyle(custom_css);
    }
    if (opt.className=='vjs-ios-skin')
        patch_controls_ios(opt);
    else
        patch_controls_default();
    new HolaSkin(this, opt);
});

}(window, window.videojs));

},{"browserify-css":171}],44:[function(_dereq_,module,exports){
/**
 * Copyright 2014 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * IMA SDK integration plugin for Video.js. For more information see
 * https://www.github.com/googleads/videojs-ima
 */

(function(factory) {
  if (typeof define === 'function' && define['amd']) {
    define(['video.js', 'videojs-contrib-ads'], function(videojs){ factory(window, document, videojs) });
  } else if (typeof exports === 'object' && typeof module === 'object') {
    var vjs = (window.videojs);
    (null);
    factory(window, document, vjs);
  } else {
    factory(window, document, videojs);
  }
})(function(window, document, videojs) {
  "use strict";

  var extend = function(obj) {
    var arg;
    var index;
    var key;
    for (index = 1; index < arguments.length; index++) {
      arg = arguments[index];
      for (key in arg) {
        if (arg.hasOwnProperty(key)) {
          obj[key] = arg[key];
        }
      }
    }
    return obj;
  };

  var ima_defaults = {
    debug: false,
    timeout: 5000,
    prerollTimeout: 100,
    adLabel: 'Advertisement',
    showControlsForAds: true,
    showControlsForJSAds: true,
    adsRenderingSettings: {
      uiElements: [],
    },
  };

  var eventTypes = (videojs.browser.IS_ANDROID || videojs.browser.IS_IOS) ? {
    click: 'touchend',
    mousedown: 'touchstart',
    mouseup: 'touchend',
    mousemove: 'touchmove'
  } : {
    click: 'click',
    mousedown: 'mousedown',
    mouseup: 'mouseup',
    mousemove: 'mousemove'
  };

  var init = function(options, readyCallback) {
    this.ima = new ImaPlugin(this, options, readyCallback);
  };

  var ImaPlugin = function(player, options, readyCallback) {
    this.player = player;
    this.viewableImpression = new ExperimentalViewableImpression(options);

    /**
     * Assigns the unique id and class names to the given element as well as the style class
     * @param element
     * @param controlName
     * @private
     */
    var assignControlAttributes_ = function(element, controlName) {
      element.id = this.controlPrefix + controlName;
      element.className = this.controlPrefix + controlName + ' ' + controlName;
    }.bind(this);

    /**
     * Returns a regular expression to test a string for the given className
     * @param className
     * @returns {RegExp}
     * @private
     */
    var getClassRegexp_ = function(className){
      // Matches on
      // (beginning of string OR NOT word char)
      // classname
      // (negative lookahead word char OR end of string)
      return new RegExp('(^|[^A-Za-z-])' + className + '((?![A-Za-z-])|$)', 'gi');
    };

    /**
     * Adds a class to the given element if it doesn't already have the class
     * @param element
     * @param classToAdd
     * @private
     */
    var addClass_ = function(element, classToAdd){
      if(getClassRegexp_(classToAdd).test(element.className)){
        return element;
      }

      return element.className = element.className.trim() + ' ' + classToAdd;
    };

    /**
     * Removes a class from the given element if it has the given class
     * @param element
     * @param classToRemove
     * @private
     */
    var removeClass_ = function(element, classToRemove){
      var classRegexp = getClassRegexp_(classToRemove);

      if(!classRegexp.test(element.className)){
        return element;
      }

      return element.className = element.className.trim().replace(classRegexp, '');
    };

    /**
     * Creates the ad container passed to the IMA SDK.
     * @private
     */
    var createAdContainer_ = function() {
      // The adContainerDiv is the DOM of the element that will house
      // the ads and ad controls.
      this.vjsControls = this.player.getChild('controlBar');
      this.adContainerDiv =
          this.vjsControls.el().parentNode.appendChild(
              document.createElement('div'));
      assignControlAttributes_(this.adContainerDiv, 'ima-ad-container');
      this.adContainerDiv.style.position = "absolute";
      this.adContainerDiv.style.zIndex = 1111;
      this.adContainerDiv.addEventListener(
          'mouseenter',
          showAdControls_,
          false);
      this.adContainerDiv.addEventListener(
          'mouseleave',
          hideAdControls_,
          false);
      createControls_();
      this.adDisplayContainer =
          new google.ima.AdDisplayContainer(this.adContainerDiv, this.contentPlayer);
      this.showAdContainer(!this.settings.manual);
    }.bind(this);

    /**
     * Creates the controls for the ad.
     * @private
     */
    var createControls_ = function() {
      this.controlsDiv = document.createElement('div');
      assignControlAttributes_(this.controlsDiv, 'ima-controls-div');
      this.controlsDiv.style.width = '100%';
      this.countdownDiv = document.createElement('div');
      assignControlAttributes_(this.countdownDiv, 'ima-countdown-div');
      this.countdownDiv.innerHTML = this.settings.adLabel;
      this.countdownDiv.style.display = this.showCountdown ? '' : 'none';
      this.seekBarDiv = document.createElement('div');
      assignControlAttributes_(this.seekBarDiv, 'ima-seek-bar-div');
      this.seekBarDiv.style.width = '100%';
      this.progressDiv = document.createElement('div');
      assignControlAttributes_(this.progressDiv, 'ima-progress-div');
      this.playPauseDiv = document.createElement('div');
      assignControlAttributes_(this.playPauseDiv, 'ima-play-pause-div');
      addClass_(this.playPauseDiv, 'ima-playing');
      this.playPauseDiv.addEventListener(
          eventTypes.click,
          onAdPlayPauseClick_,
          false);
      this.muteDiv = document.createElement('div');
      assignControlAttributes_(this.muteDiv, 'ima-mute-div');
      addClass_(this.muteDiv, 'ima-non-muted');
      this.muteDiv.addEventListener(
          eventTypes.click,
          onAdMuteClick_,
          false);
      this.sliderDiv = document.createElement('div');
      assignControlAttributes_(this.sliderDiv, 'ima-slider-div');
      this.sliderDiv.addEventListener(
          eventTypes.mousedown,
          onAdVolumeSliderMouseDown_,
          false);
      this.sliderLevelDiv = document.createElement('div');
      assignControlAttributes_(this.sliderLevelDiv, 'ima-slider-level-div');
      this.fullscreenDiv = document.createElement('div');
      assignControlAttributes_(this.fullscreenDiv, 'ima-fullscreen-div');
      addClass_(this.fullscreenDiv, 'ima-non-fullscreen');
      this.fullscreenDiv.addEventListener(
          eventTypes.click,
          onAdFullscreenClick_,
          false);
      this.adContainerDiv.appendChild(this.controlsDiv);
      this.controlsDiv.appendChild(this.seekBarDiv);
      this.controlsDiv.appendChild(this.playPauseDiv);
      this.controlsDiv.appendChild(this.muteDiv);
      this.controlsDiv.appendChild(this.sliderDiv);
      this.controlsDiv.appendChild(this.fullscreenDiv);
      this.seekBarDiv.appendChild(this.progressDiv);
      this.sliderDiv.appendChild(this.sliderLevelDiv);
      if (this.settings.vjsControls) {
        this.initVjsControls();
        if (this.player.dvr)
          this.player.one('dvrReady', this.initVjsControls.bind(this));
        this.controlsDiv.style.display = 'none';
        this.vjsControls.el().appendChild(this.countdownDiv);
      } else {
        this.controlsDiv.appendChild(this.countdownDiv);
      }
    }.bind(this);

    this.showAdContainer = function(show) {
      this.adContainerDiv.style.display = show ? 'block' : 'none';
      this.player.toggleClass('vjs-ima-ad', show);
    }.bind(this);

    /**
     * Initializes the AdDisplayContainer. On mobile, this must be done as a
     * result of user action.
     */
    this.initializeAdDisplayContainer = function() {
      this.adDisplayContainerInitialized = true;
      this.adDisplayContainer.initialize();
    }.bind(this);


    /**
     * Creates the AdsRequest and request ads through the AdsLoader.
     */
    this.requestAds = function() {
      if (!this.adDisplayContainerInitialized) {
        this.adDisplayContainer.initialize();
      }

      var adsRequest = new google.ima.AdsRequest();
      var customLoad;
      if (this.settings.adTagUrl) {
        adsRequest.adTagUrl = this.settings.adTagUrl;
      } else {
        var adsResponse = this.settings.adsResponse;
        customLoad = this.viewableImpression.initialize(adsResponse,
          function(customAdsResponse) {
            adsRequest.adsResponse = customAdsResponse || adsResponse;
            this.adsLoader.requestAds(adsRequest);
          }.bind(this));

        if (!customLoad) {
          adsRequest.adsResponse = this.settings.adsResponse;
        }
      }
      if (this.settings.forceNonLinearFullSlot) {
        adsRequest.forceNonLinearFullSlot = true;
      }

      adsRequest.linearAdSlotWidth = this.getPlayerWidth();
      adsRequest.linearAdSlotHeight = this.getPlayerHeight();
      adsRequest.nonLinearAdSlotWidth =
          this.settings.nonLinearWidth || this.getPlayerWidth();
      adsRequest.nonLinearAdSlotHeight =
          this.settings.nonLinearHeight || this.getPlayerHeight();
      adsRequest.setAdWillAutoPlay(this.player.autoplay());
      adsRequest.setAdWillPlayMuted(this.player.muted() ||
          this.player.volume()==0);
      if (!customLoad) {
        this.adsLoader.requestAds(adsRequest);
      }
    }.bind(this);

    /**
     * Listener for the ADS_MANAGER_LOADED event. Creates the AdsManager,
     * sets up event listeners, and triggers the 'adsready' event for
     * videojs-ads-contrib.
     * @private
     */
    var onAdsManagerLoaded_ = function(adsManagerLoadedEvent) {
      this.adsManager = adsManagerLoadedEvent.getAdsManager(
          this.contentPlayheadTracker, this.adsRenderingSettings);

      this.viewableImpression.attach(this.adContainerDiv, this.adsManager);

      this.adsManager.addEventListener(
          google.ima.AdErrorEvent.Type.AD_ERROR,
          onAdError_);
      this.adsManager.addEventListener(
          google.ima.AdEvent.Type.AD_BREAK_READY,
          onAdBreakReady_);
      this.adsManager.addEventListener(
          google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED,
          this.onContentPauseRequested_);
      this.adsManager.addEventListener(
          google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,
          this.onContentResumeRequested_);
      this.adsManager.addEventListener(
          google.ima.AdEvent.Type.ALL_ADS_COMPLETED,
          onAllAdsCompleted_);

      this.adsManager.addEventListener(
          google.ima.AdEvent.Type.LOADED,
          onAdLoaded_);
      this.adsManager.addEventListener(
          google.ima.AdEvent.Type.STARTED,
          onAdStarted_);
      this.adsManager.addEventListener(
          google.ima.AdEvent.Type.CLICK,
          onAdPlayPauseClick_);
      this.adsManager.addEventListener(
          google.ima.AdEvent.Type.COMPLETE,
          this.onAdComplete_);
      this.adsManager.addEventListener(
          google.ima.AdEvent.Type.SKIPPED,
          this.onAdComplete_);
      this.adsManager.addEventListener(
          google.ima.AdEvent.Type.PAUSED,
          this.onAdPaused_);
      this.adsManager.addEventListener(
          google.ima.AdEvent.Type.RESUMED,
          this.onAdResumed_);
      var eventsMap = {
          'load': google.ima.AdEvent.Type.LOADED,
          'ad-started': google.ima.AdEvent.Type.STARTED,
          'click': google.ima.AdEvent.Type.CLICK,
          'ad-ended': google.ima.AdEvent.Type.COMPLETE,
          'ad-skipped': google.ima.AdEvent.Type.SKIPPED,
          'first-quartile': google.ima.AdEvent.Type.FIRST_QUARTILE,
          'midpoint': google.ima.AdEvent.Type.MIDPOINT,
          'third-quartile': google.ima.AdEvent.Type.THIRD_QUARTILE,
          'impression': google.ima.AdEvent.Type.IMPRESSION,
          'pause': google.ima.AdEvent.Type.PAUSED,
          'play': google.ima.AdEvent.Type.RESUMED,
          'mute': google.ima.AdEvent.Type.VOLUME_MUTED,
      };
      Object.keys(eventsMap).forEach(function(event){
          this.adsManager.addEventListener(eventsMap[event], function(){
              this.player.trigger('ads-'+event);
          }.bind(this));
      }.bind(this));

      if (!this.autoPlayAdBreaks) {
        try {
          var initWidth = this.getPlayerWidth();
          var initHeight = this.getPlayerHeight();
          this.adsManagerDimensions.width = initWidth;
          this.adsManagerDimensions.height = initHeight;
          this.adsManager.init(
              initWidth,
              initHeight,
              google.ima.ViewMode.NORMAL);
          this.adsManager.setVolume(this.player.muted() ? 0 : this.player.volume());
        } catch (adError) {
          onAdError_(adError);
        }
      }

      var cuepoints = this.adsManager.getCuePoints();
      var foundpreroll = !cuepoints.length; // no playlist, just preroll
      var foundpostroll = false;;
      cuepoints.forEach(function(offset){
          if (!offset)
              foundpreroll = true;
          else if (offset==-1)
              foundpostroll = true;
      });
      if (!foundpreroll)
          this.player.trigger('nopreroll');
      if (!foundpostroll)
          this.player.trigger('nopostroll');
      if (cuepoints.length)
          this.player.trigger('ads-cuepoints', cuepoints);

      this.player.trigger('adsready');
    }.bind(this);

    /**
     * DEPRECATED: Use startFromReadyCallback
     * Start ad playback, or content video playback in the absence of a
     * pre-roll.
     */
    this.start = function() {
      window.console.log(
          'WARNING: player.ima.start is deprecated. Use ' +
              'player.ima.startFromReadyCallback instead.');
    };

    /**
     * Start ad playback, or content video playback in the absence of a
     * pre-roll. **NOTE**: This method only needs to be called if you provide
     * your own readyCallback as the second parameter to player.ima(). If you
     * only provide options and do not provide your own readyCallback,
     * **DO NOT** call this method. If you do provide your own readyCallback,
     * you should call this method in the last line of that callback. For more
     * info, see this method's usage in our advanced and playlist examples.
     */
    this.startFromReadyCallback = function() {
      if (this.autoPlayAdBreaks && this.adsManager) {
        try {
          this.adsManager.init(
              this.getPlayerWidth(),
              this.getPlayerHeight(),
              google.ima.ViewMode.NORMAL);
          this.adsManager.setVolume(this.player.muted() ? 0 : this.player.volume());
          this.adsManager.start();
        } catch (adError) {
          onAdError_(adError);
        }
      }
    }.bind(this);

    /**
     * Listener for errors fired by the AdsLoader.
     * @param {google.ima.AdErrorEvent} event The error event thrown by the
     *     AdsLoader. See
     *     https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdError.Type
     * @private
     */
    var onAdsLoaderError_ = function(event) {
      window.console.log('AdsLoader error: ' + event.getError());
      this.showAdContainer(false);
      if (this.adsManager) {
        this.adsManager.destroy();
      }
      this.player.trigger({type: 'adserror', data: { AdError: event.getError(), AdErrorEvent: event }});
    }.bind(this);

    /**
     * Listener for errors thrown by the AdsManager.
     * @param {google.ima.AdErrorEvent} adErrorEvent The error event thrown by
     *     the AdsManager.
     * @private
     */
    var onAdError_ = function(adErrorEvent) {
      var errorMessage = adErrorEvent.getError !== undefined ? adErrorEvent.getError() : adErrorEvent.stack;
      window.console.log('Ad error: ' + errorMessage);
      this.adsActive = false;
      this.adPlaying = false;
      this.restoreLoop();
      this.vjsControls.show();
      this.adsManager.destroy();
      this.showAdContainer(false);
      this.updateFullscreenButton();
      this.updateVjsControls();
      this.player.trigger({ type: 'adserror', data: { AdError: errorMessage, AdErrorEvent: adErrorEvent }});
    }.bind(this);

    /**
     * Listener for AD_BREAK_READY. Passes event on to publisher's listener.
     * @param {google.ima.AdEvent} adEvent AdEvent thrown by the AdsManager.
     * @private
     */
    var onAdBreakReady_ = function(adEvent) {
      this.adBreakReadyListener(adEvent);
    }.bind(this);

    /**
     * Called by publishers in manual ad break playback mode to start an ad
     * break.
     */
    this.playAdBreak = function() {
      if (!this.autoPlayAdBreaks) {
        this.adsManager.start();
      }
    }.bind(this);

    this.resetLoop = function() {
      this.contentLoop = this.contentPlayer && this.contentPlayer.loop;
      if (this.contentLoop) {
        this.contentPlayer.loop = false;
      }
    }.bind(this);

    this.restoreLoop = function() {
      if (this.contentLoop) {
        this.contentPlayer.loop = true;
        this.contentLoop = false;
      }
    }.bind(this);

    /**
     * Pauses the content video and displays the ad container so ads can play.
     * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
     * @private
     */
    this.onContentPauseRequested_ = function(adEvent) {
      if (this.player.isFullscreen() && !this.isFullscreenAdSupported()) {
        this.player.exitFullscreen();
      }
      this.contentSource = this.player.currentSrc();
      this.resetLoop();
      this.player.off('contentended', this.localContentEndedListener);
      this.player.ads.startLinearAdMode();
      this.showAdContainer(true);

      var contentType = adEvent.getAd().getContentType();
      if (!this.settings.vjsControls || !this.settings.showControlsForAds){
        if (!this.settings.showControlsForAds
            || ((contentType === 'application/javascript') && !this.settings.showControlsForJSAds)) {
          this.controlsDiv.style.display = 'none';
        } else {
          this.controlsDiv.style.display = 'block';
        }
        this.vjsControls.hide();
      }
      showPlayButton();
      this.player.pause();
      this.adsActive = true;
      this.adPlaying = true;
      this.updateFullscreenButton();
      this.updateVjsControls();
    }.bind(this);

    /**
     * Resumes content video and hides the ad container.
     * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
     * @private
     */
    this.onContentResumeRequested_ = function(adEvent) {
      this.contentResumeTimer = clearTimeout(this.contentResumeTimer);
      this.restoreLoop();
      this.adsActive = false;
      this.adPlaying = false;
      this.player.on('contentended', this.localContentEndedListener);
      if (this.currentAd == null || // hide for post-roll only playlist
          this.currentAd.isLinear()) { // don't hide for non-linear ads
        this.showAdContainer(false);
      }
      this.vjsControls.show();
      this.player.ads.endLinearAdMode();
      this.countdownDiv.innerHTML = '';
      this.updateFullscreenButton();
      this.updateVjsControls();
    }.bind(this);

    /**
     * Records that ads have completed and calls contentAndAdsEndedListeners
     * if content is also complete.
     * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
     * @private
     */
    var onAllAdsCompleted_ = function(adEvent) {
      this.allAdsCompleted = true;
      this.showAdContainer(false);
      if (this.contentComplete == true) {
        if (this.contentPlayer.src && !/^blob:/.test(this.contentPlayer.src) &&
          this.contentSource && this.contentPlayer.src != this.contentSource) {
          this.player.src(this.contentSource);
        }
        for (var index in this.contentAndAdsEndedListeners) {
          this.contentAndAdsEndedListeners[index]();
        }
      }
    }.bind(this);

    /**
     * Starts the content video when a non-linear ad is loaded.
     * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
     * @private
     */
   var onAdLoaded_ = function(adEvent) {
      if (!adEvent.getAd().isLinear() && !this.player.ended()) {
        this.player.ads.endLinearAdMode();
        this.player.play();
      }
    }.bind(this);

    /**
     * Starts the interval timer to check the current ad time when an ad starts
     * playing.
     * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
     * @private
     */
    var onAdStarted_ = function(adEvent) {
      this.currentAd = adEvent.getAd();
      if (this.currentAd.isLinear()) {
        this.adTrackingTimer = setInterval(
            onAdPlayheadTrackerInterval_, 250);
        // Don't bump container when controls are shown
        removeClass_(this.adContainerDiv, 'bumpable-ima-ad-container');
      } else {
        // Bump container when controls are shown
        addClass_(this.adContainerDiv, 'bumpable-ima-ad-container');
        this.player.addClass('vjs-ima-non-linear');
        this.showAdContainer(true);
      }
    }.bind(this);

    /**
     * Clears the interval timer for current ad time when an ad completes.
     * @param {google.ima.AdEvent} adEvent The AdEvent thrown by the AdsManager.
     * @private
     */
    this.onAdComplete_ = function(adEvent) {
      if (this.currentAd.isLinear()) {
        clearInterval(this.adTrackingTimer);
        var pod = this.currentAd.getAdPodInfo();
        if (pod && pod.getAdPosition()<pod.getTotalAds())
            return;
        // this is the final ad so we excpect ima sdk to trigger
        // CONTENT_RESUME_REQUESTED, but for some reason it isn't triggered
        // reliably on iOS, so we fake it
        this.contentResumeTimer = setTimeout(function(){
            this.onContentResumeRequested_(null);
        }.bind(this), 1000);
      } else {
        this.player.removeClass('vjs-ima-non-linear');
      }
    }.bind(this);

    this.onAdPaused_ = function(adEvent) {
      showPauseButton();
      this.adPlaying = false;
    }.bind(this);

    this.onAdResumed_ = function(adEvent) {
      showPlayButton();
      this.adPlaying = true;
    }.bind(this);

    var formatTime = function(time) {
      var m = Math.floor(time / 60);
      var s = Math.floor(time % 60);
      if (s.toString().length < 2) {
        s = '0' + s;
      }
      return m + ':' + s;
    };

    /**
     * Gets the current time and duration of the ad and calls the method to
     * update the ad UI.
     * @private
     */
    var onAdPlayheadTrackerInterval_ = function() {
      var remainingTime = this.adsManager.getRemainingTime();
      var duration =  this.currentAd.getDuration();
      var currentTime = duration - remainingTime;
      currentTime = currentTime > 0 ? currentTime : 0;
      var isPod = false;
      var totalAds = 0;
      var adPosition;
      if (this.currentAd.getAdPodInfo()) {
        isPod = true;
        adPosition = this.currentAd.getAdPodInfo().getAdPosition();
        totalAds = this.currentAd.getAdPodInfo().getTotalAds();
      }

      // Update countdown timer data
      var podCount = ': ';
      if (isPod && (totalAds > 1)) {
        podCount = ' (' + adPosition + ' of ' + totalAds + '): ';
      }
      this.countdownDiv.innerHTML =
          this.settings.adLabel + podCount + formatTime(remainingTime);

      // Update UI
      var playProgressRatio = currentTime / duration;
      var playProgressPercent = playProgressRatio * 100;
      this.progressDiv.style.width = playProgressPercent + '%';
      this.updateVjsControls();
    }.bind(this);

    this.getPlayerWidth = function() {
      return this.player.el().offsetWidth || this.player.width();
    }.bind(this);

    this.getPlayerHeight = function() {
      return this.player.el().offsetHeight || this.player.height();
    }.bind(this);

    /**
     * Hides the ad controls on mouseout.
     * @private
     */
    var hideAdControls_ = function() {
      this.controlsDiv.style.height = '14px';
      this.playPauseDiv.style.display = 'none';
      this.muteDiv.style.display = 'none';
      this.sliderDiv.style.display = 'none';
      this.fullscreenDiv.style.display = 'none';
    }.bind(this);

    /**
     * Shows ad controls on mouseover.
     * @private
     */
    var showAdControls_ = function() {
      this.controlsDiv.style.height = '37px';
      this.playPauseDiv.style.display = 'block';
      this.muteDiv.style.display = 'block';
      this.sliderDiv.style.display = 'block';
      this.fullscreenDiv.style.display = 'block';
    }.bind(this);

    /**
     * Show pause and hide play button
     */
    var showPauseButton = function() {
      addClass_(this.playPauseDiv, 'ima-paused');
      removeClass_(this.playPauseDiv, 'ima-playing');
    }.bind(this);

    /**
     * Show play and hide pause button
     */
    var showPlayButton = function() {
      addClass_(this.playPauseDiv, 'ima-playing');
      removeClass_(this.playPauseDiv, 'ima-paused');
    }.bind(this);

    /**
     * Listener for clicks on the play/pause button during ad playback.
     * @private
     */
    var onAdPlayPauseClick_ = function() {
      if (this.adPlaying) {
        showPauseButton();
        this.adsManager.pause();
        this.adPlaying = false;
      } else {
        showPlayButton();
        this.adsManager.resume();
        this.adPlaying = true;
      }
    }.bind(this);

    /**
     * Listener for clicks on the mute button during ad playback.
     * @private
     */
    var onAdMuteClick_ = function() {
      if (this.adMuted) {
        addClass_(this.muteDiv, 'ima-non-muted');
        removeClass_(this.muteDiv, 'ima-muted');
        this.adsManager.setVolume(1);
        // Bubble down to content player
        this.player.muted(false);
        this.adMuted = false;
        this.sliderLevelDiv.style.width = this.player.volume() * 100 + "%";
      } else {
        addClass_(this.muteDiv, 'ima-muted');
        removeClass_(this.muteDiv, 'ima-non-muted');
        this.adsManager.setVolume(0);
        // Bubble down to content player
        this.player.muted(true);
        this.adMuted = true;
        this.sliderLevelDiv.style.width = "0%";
      }
    }.bind(this);

    /* Listener for mouse down events during ad playback. Used for volume.
     * @private
     */
    var onAdVolumeSliderMouseDown_ = function() {
       document.addEventListener(eventTypes.mouseup, onMouseUp_, false);
       document.addEventListener(eventTypes.mousemove, onMouseMove_, false);
    };

    /* Mouse movement listener used for volume slider.
     * @private
     */
    var onMouseMove_ = function(event) {
      setVolumeSlider_(event);
    };

    /* Mouse release listener used for volume slider.
     * @private
     */
    var onMouseUp_ = function(event) {
      setVolumeSlider_(event);
      document.removeEventListener(eventTypes.mousemove, onMouseMove_);
      document.removeEventListener(eventTypes.mouseup, onMouseUp_);
    };

    /* Utility function to set volume and associated UI
     * @private
     */
    var setVolumeSlider_ = function(event) {
      var clientX = event.changedTouches ? event.changedTouches[0].clientX :
        event.clientX;
      var percent = (clientX - this.sliderDiv.getBoundingClientRect().left) /
        this.sliderDiv.offsetWidth;
      percent *= 100;
      //Bounds value 0-100 if mouse is outside slider region.
      percent = Math.min(Math.max(percent, 0), 100);
      this.sliderLevelDiv.style.width = percent + "%";
      this.player.volume(percent / 100); //0-1
      this.adsManager.setVolume(percent / 100);
      if (this.player.volume() == 0) {
        addClass_(this.muteDiv, 'ima-muted');
        removeClass_(this.muteDiv, 'ima-non-muted');
        this.player.muted(true);
        this.adMuted = true;
      }
      else
      {
        addClass_(this.muteDiv, 'ima-non-muted');
        removeClass_(this.muteDiv, 'ima-muted');
        this.player.muted(false);
        this.adMuted = false;
      }
    }.bind(this);

    /**
     * Listener for clicks on the fullscreen button during ad playback.
     * @private
     */
    var onAdFullscreenClick_ = function() {
      if (this.player.isFullscreen()) {
        this.player.exitFullscreen();
      } else {
        this.player.requestFullscreen();
      }
    }.bind(this);

    /**
     * Listens for the video.js player to change its fullscreen status. This
     * keeps the fullscreen-ness of the AdContainer in sync with the player.
     * @private
     */
    var onFullscreenChange_ = function() {
      if (this.player.isFullscreen()) {
        addClass_(this.fullscreenDiv, 'ima-fullscreen');
        removeClass_(this.fullscreenDiv, 'ima-non-fullscreen');
        if (this.adsManager) {
          this.adsManager.resize(
              window.screen.width,
              window.screen.height,
              google.ima.ViewMode.FULLSCREEN);
        }
      } else {
        addClass_(this.fullscreenDiv, 'ima-non-fullscreen');
        removeClass_(this.fullscreenDiv, 'ima-fullscreen');
        if (this.adsManager) {
          this.adsManager.resize(
              this.getPlayerWidth(),
              this.getPlayerHeight(),
              google.ima.ViewMode.NORMAL);
        }
      }
    }.bind(this);

    /**
     * Listens for the video.js player to change its volume. This keeps the ad
     * volume in sync with the content volume if the volume of the player is
     * changed while content is playing
     * @private
     */
    var onVolumeChange_ = function() {
      var newVolume = this.player.muted() ? 0 : this.player.volume();
      if (this.adsManager) {
        this.adsManager.setVolume(newVolume);
      }
      // Update UI
      if (newVolume == 0) {
        this.adMuted = true;
        addClass_(this.muteDiv, 'ima-muted');
        removeClass_(this.muteDiv, 'ima-non-muted');
        this.sliderLevelDiv.style.width = '0%';
      } else {
        this.adMuted = false;
        addClass_(this.muteDiv, 'ima-non-muted');
        removeClass_(this.muteDiv, 'ima-muted');
        this.sliderLevelDiv.style.width = newVolume * 100 + '%';
      }
    }.bind(this);

    /**
     * Seeks content to 00:00:00. This is used as an event handler for the
     * loadedmetadata event, since seeking is not possible until that event has
     * fired.
     * @private
     */
    var seekContentToZero_ = function() {
      this.player.off('loadedmetadata', seekContentToZero_);
      this.player.currentTime(0);
    }.bind(this);

    /**
     * Seeks content to 00:00:00 and starts playback. This is used as an event
     * handler for the loadedmetadata event, since seeking is not possible until
     * that event has fired.
     * @private
     */
    var playContentFromZero_ = function() {
      this.player.off('loadedmetadata', playContentFromZero_);
      this.player.currentTime(0);
      this.player.play();
    }.bind(this);

    /**
     * Destroys the AdsManager, sets it to null, and calls contentComplete to
     * reset correlators. Once this is done it requests ads again to keep the
     * inventory available.
     * @private
     */
    var resetIMA_ = function() {
      this.adsActive = false;
      this.adPlaying = false;
      this.restoreLoop();
      this.player.on('contentended', this.localContentEndedListener);
      if (this.currentAd && this.currentAd.isLinear()) {
        this.showAdContainer(false);
      }
      this.vjsControls.show();
      this.player.ads.endLinearAdMode();
      this.updateFullscreenButton();
      this.updateVjsControls();
      if (this.adTrackingTimer) {
        // If this is called while an ad is playing, stop trying to get that
        // ad's current time.
        clearInterval(this.adTrackingTimer);
      }
      // Reset the content time we give the SDK. Fixes an issue where requesting
      // VMAP followed by VMAP would play the second mid-rolls as pre-rolls if
      // the first playthrough of the video passed the second response's
      // mid-roll time.
      this.contentPlayheadTracker.currentTime = 0;
      if (this.adsManager) {
        this.adsManager.destroy();
        this.adsManager = null;
      }
      if (this.adsLoader && !this.contentComplete) {
        this.adsLoader.contentComplete();
      }
      this.contentComplete = false;
      this.allAdsCompleted = false;
    }.bind(this);

    /**
     * Ads an EventListener to the AdsManager. For a list of available events,
     * see
     * https://developers.google.com/interactive-media-ads/docs/sdks/html5/v3/apis#ima.AdEvent.Type
     * @param {google.ima.AdEvent.Type} event The AdEvent.Type for which to listen.
     * @param {function} callback The method to call when the event is fired.
     */
    this.addEventListener = function(event, callback) {
      if (this.adsManager) {
        this.adsManager.addEventListener(event, callback);
      }
    }.bind(this);

    /**
     * Returns the instance of the AdsManager.
     * @return {google.ima.AdsManager} The AdsManager being used by the plugin.
     */
    this.getAdsManager = function() {
      return this.adsManager;
    }.bind(this);

    /**
     * DEPRECATED: Use setContentWithAdTag.
     * Sets the content of the video player. You should use this method instead
     * of setting the content src directly to ensure the proper ad tag is
     * requested when the video content is loaded.
     * @param {?string} contentSrc The URI for the content to be played. Leave
     *     blank to use the existing content.
     * @param {?string} adTag The ad tag to be requested when the content loads.
     *     Leave blank to use the existing ad tag.
     * @param {?boolean} playOnLoad True to play the content once it has loaded,
     *     false to only load the content but not start playback.
     */
    this.setContent = function(contentSrc, adTag, playOnLoad) {
      window.console.log(
          'WARNING: player.ima.setContent is deprecated. Use ' +
              'player.ima.setContentWithAdTag instead.');
      this.setContentWithAdTag(contentSrc, adTag, playOnLoad);
    }.bind(this);

    /**
     * Sets the content of the video player. You should use this method instead
     * of setting the content src directly to ensure the proper ad tag is
     * requested when the video content is loaded.
     * @param {?string} contentSrc The URI for the content to be played. Leave
     *     blank to use the existing content.
     * @param {?string} adTag The ad tag to be requested when the content loads.
     *     Leave blank to use the existing ad tag.
     * @param {?boolean} playOnLoad True to play the content once it has loaded,
     *     false to only load the content but not start playback.
     */
    this.setContentWithAdTag = function(contentSrc, adTag, playOnLoad) {
      resetIMA_();
      this.settings.adTagUrl = adTag ? adTag : this.settings.adTagUrl;
      changeSource_(contentSrc, playOnLoad);
    }.bind(this);

    /**
     * Sets the content of the video player. You should use this method instead
     * of setting the content src directly to ensure the proper ads response is
     * used when the video content is loaded.
     * @param {?string} contentSrc The URI for the content to be played. Leave
     *     blank to use the existing content.
     * @param {?string} adsResponse The ads response to be requested when the
     *     content loads. Leave blank to use the existing ads response.
     * @param {?boolean} playOnLoad True to play the content once it has loaded,
     *     false to only load the content but not start playback.
     */
    this.setContentWithAdsResponse = function(contentSrc, adsResponse, playOnLoad) {
      resetIMA_();
      this.settings.adsResponse = adsResponse ? adsResponse : this.settings.adsResponse;
      changeSource_(contentSrc, playOnLoad);
    }.bind(this);

    /**
     * Plays an ad immediately
     * @param {?string} adTag The ad tag to be requested.
     *     Leave blank to use the existing ad tag.
     */
    this.playAd = function(adTag) {
      resetIMA_();
      this.settings.adTagUrl = adTag ? adTag : this.settings.adTagUrl;
      this.showAdContainer(true);
      this.requestAds();
    }.bind(this);

    /**
     * Changes the player source.
     * @param {?string} contentSrc The URI for the content to be played. Leave
     *     blank to use the existing content.
     * @param {?boolean} playOnLoad True to play the content once it has loaded,
     *     false to only load the content but not start playback.
     * @private
     */
    var changeSource_ = function(contentSrc, playOnLoad) {
      // Only try to pause the player when initialised with a source already
      if (!!this.player.currentSrc()) {
        this.player.currentTime(0);
        this.player.pause();
      }
      if (contentSrc) {
        this.player.src(contentSrc);
      }
      if (playOnLoad) {
        this.player.on('loadedmetadata', playContentFromZero_);
      } else {
        this.player.on('loadedmetadata', seekContentToZero_);
      }
    }.bind(this);

    /**
     * Adds a listener for the 'contentended' event of the video player. This should be
     * used instead of setting an 'contentended' listener directly to ensure that the
     * ima can do proper cleanup of the SDK before other event listeners
     * are called.
     * @param {function} listener The listener to be called when content completes.
     */
    this.addContentEndedListener = function(listener) {
      this.contentEndedListeners.push(listener);
    }.bind(this);

    /**
     * Adds a listener that will be called when content and all ads have
     * finished playing.
     * @param {function} listener The listener to be called when content and ads complete.
     */
    this.addContentAndAdsEndedListener = function(listener) {
      this.contentAndAdsEndedListeners.push(listener);
    }.bind(this);

    /**
     * Sets the listener to be called to trigger manual ad break playback.
     * @param {function} listener The listener to be called to trigger manual ad break playback.
     */
    this.setAdBreakReadyListener = function(listener) {
      this.adBreakReadyListener = listener;
    }.bind(this);

    /**
     * Pauses the ad.
     */
    this.pauseAd = function() {
      if (this.adsActive && this.adPlaying) {
        showPauseButton();
        this.adsManager.pause();
        this.adPlaying = false;
      }
    }.bind(this);

    /**
     * Resumes the ad.
     */
    this.resumeAd = function() {
      if (this.adsActive && !this.adPlaying) {
        showPlayButton();
        this.adsManager.resume();
        this.adPlaying = true;
      }
    }.bind(this);

    /**
     * Set up intervals to check for seeking and update current video time.
     * @private
     */
    var setUpPlayerIntervals_ = function() {
      this.updateTimeIntervalHandle =
          setInterval(updateCurrentTime_, this.seekCheckInterval);
      this.seekCheckIntervalHandle =
          setInterval(checkForSeeking_, this.seekCheckInterval);
      this.resizeCheckIntervalHandle =
          setInterval(checkForResize_, this.resizeCheckInterval);
    }.bind(this);

    var updateLiveOffset_ = function() {
      var cur = this.player.currentTime();
      if (!cur || this.player.ads.state!='content-playback')
        return;
      if (this.player.duration()!=Infinity || cur<0)
        cur = 0;
      this.contentPlayheadTracker.liveOffset = cur;
      this.player.off('timeupdate', updateLiveOffset_);
    }.bind(this);

    /**
     * Updates the current time of the video
     * @private
     */
    var updateCurrentTime_ = function() {
      if (this.player.ads.state=='content-playback' &&
          !this.contentPlayheadTracker.seeking &&
          this.contentPlayheadTracker.liveOffset>=0) {
        this.contentPlayheadTracker.currentTime = this.player.currentTime()-
            this.contentPlayheadTracker.liveOffset;
      }
    }.bind(this);

    /**
     * Detects when the user is seeking through a video.
     * This is used to prevent mid-rolls from playing while a user is seeking.
     *
     * There *is* a seeking property of the HTML5 video element, but it's not
     * properly implemented on all platforms (e.g. mobile safari), so we have to
     * check ourselves to be sure.
     *
     * @private
     */
    var checkForSeeking_ = function() {
      if (this.player.ads.state!='content-playback')
          return;
      var tempCurrentTime = this.player.currentTime();
      var diff = (tempCurrentTime - this.contentPlayheadTracker.previousTime) * 1000;
      if (Math.abs(diff) > this.seekCheckInterval + this.seekThreshold) {
        this.contentPlayheadTracker.seeking = true;
      } else {
        this.contentPlayheadTracker.seeking = false;
      }
      this.contentPlayheadTracker.previousTime = this.player.currentTime();
    }.bind(this);

    /**
     * Detects when the player is resized (for fluid support) and resizes the
     * ads manager to match.
     *
     * @private
     */
    var checkForResize_ = function() {
      var currentWidth = this.getPlayerWidth();
      var currentHeight = this.getPlayerHeight();

      if (this.adsManager && (currentWidth != this.adsManagerDimensions.width ||
          currentHeight != this.adsManagerDimensions.height)) {
        this.adsManagerDimensions.width = currentWidth;
        this.adsManagerDimensions.height = currentHeight;
        this.adsManager.resize(currentWidth, currentHeight, google.ima.ViewMode.NORMAL);
      }
    }.bind(this);

    /**
     * Changes the flag to show or hide the ad countdown timer.
     *
     * @param {boolean} showCountdownIn Show or hide the countdown timer.
     */
    this.setShowCountdown = function(showCountdownIn) {
      this.showCountdown = showCountdownIn;
      this.countdownDiv.style.display = this.showCountdown ? '' : 'none';
    }.bind(this);

    /**
     * Current plugin version.
     */
    this.VERSION = '0.2.0';

    /**
     * Stores user-provided settings.
     */
    this.settings;

    /**
     * Used to prefix videojs ima
     */
    this.controlPrefix;

    /**
     * Video element playing content.
     */
    this.contentPlayer;

    /**
     * Boolean flag to show or hide the ad countdown timer.
     */
    this.showCountdown;

    /**
     * Boolena flag to enable manual ad break playback.
     */
    this.autoPlayAdBreaks;

    /**
     * Video.js control bar.
     */
    this.vjsControls;

    /**
     * Div used as an ad container.
     */
    this.adContainerDiv;

    /**
     * Div used to display ad controls.
     */
    this.controlsDiv;

    /**
     * Div used to display ad countdown timer.
     */
    this.countdownDiv;

    /**
     * Div used to display add seek bar.
     */
    this.seekBarDiv;

    /**
     * Div used to display ad progress (in seek bar).
     */
    this.progressDiv;

    /**
     * Div used to display ad play/pause button.
     */
    this.playPauseDiv;

    /**
     * Div used to display ad mute button.
     */
    this.muteDiv;

    /**
     * Div used by the volume slider.
     */
    this.sliderDiv;

    /**
     * Volume slider level visuals
     */
    this.sliderLevelDiv;

    /**
     * Div used to display ad fullscreen button.
     */
    this.fullscreenDiv;

    /**
     * IMA SDK AdDisplayContainer.
     */
    this.adDisplayContainer;

    /**
     * True if the AdDisplayContainer has been initialized. False otherwise.
     */
    this.adDisplayContainerInitialized = false;

    /**
     * IMA SDK AdsLoader
     */
    this.adsLoader;

    /**
     * IMA SDK AdsManager
     */
    this.adsManager;

    /**
     * IMA SDK AdsRenderingSettings.
     */
    this.adsRenderingSettings = null;

    /**
     * Ad tag URL. Should return VAST, VMAP, or ad rules.
     */
    this.adTagUrl;

    /**
     * VAST, VMAP, or ad rules response. Used in lieu of fetching a response
     * from an ad tag URL.
     */
    this.adsResponse;

    /**
     * Current IMA SDK Ad.
     */
    this.currentAd;

    /**
     * Timer used to track content progress.
     */
    this.contentTrackingTimer;

    /**
     * Timer used to track ad progress.
     */
    this.adTrackingTimer;

    /**
     * True if ads are currently displayed, false otherwise.
     * True regardless of ad pause state if an ad is currently being displayed.
     */
    this.adsActive = false;

    /**
     * True if ad is currently playing, false if ad is paused or ads are not
     * currently displayed.
     */
    this.adPlaying = false;

    /**
     * True if the ad is muted, false otherwise.
     */
    this.adMuted = false;

    /**
     * True if our content video has completed, false otherwise.
     */
    this.contentComplete = false;

    /**
     * True if ALL_ADS_COMPLETED has fired, false until then.
     */
     this.allAdsCompleted = false;

    /**
     * Handle to interval that repeatedly updates current time.
     */
    this.updateTimeIntervalHandle;

    /**
     * Handle to interval that repeatedly checks for seeking.
     */
    this.seekCheckIntervalHandle;

    /**
     * Interval (ms) on which to check if the user is seeking through the
     * content.
     */
    this.seekCheckInterval = 1000;

    /**
     * Handle to interval that repeatedly checks for player resize.
     */
    this.resizeCheckIntervalHandle;

    /**
     * Interval (ms) to check for player resize for fluid support.
     */
    this.resizeCheckInterval = 250;

    /**
     * Threshold by which to judge user seeking. We check every 1000 ms to see
     * if the user is seeking. In order for us to decide that they are *not*
     * seeking, the content video playhead must only change by 900-1100 ms
     * between checks. Any greater change and we assume the user is seeking
     * through the video.
     */
    this.seekThreshold = 100;

    /**
     * Stores data for the content playhead tracker.
     */
    this.contentPlayheadTracker = {
      currentTime: 0,
      previousTime: 0,
      seeking: false,
      duration: 0,
      liveOffset: -1
    };

    /**
     * Stores data for the ad playhead tracker.
     */
    this.adPlayheadTracker = {
      currentTime: 0,
      duration: 0,
      isPod: false,
      adPosition: 0,
      totalAds: 0
    };

    /**
     * Stores the dimensions for the ads manager.
     */
    this.adsManagerDimensions = {
      width: 0,
      height: 0
    };

    /**
     * Content ended listeners passed by the publisher to the plugin. Publishers
     * should allow the plugin to handle content ended to ensure proper support
     * of custom ad playback.
     */
    this.contentEndedListeners = [];

    /**
     * Content and ads ended listeners passed by the publisher to the plugin.
     * These will be called when the plugin detects that content *and all
     * ads* have completed. This differs from the contentEndedListeners in that
     * contentEndedListeners will fire between content ending and a post-roll
     * playing, whereas the contentAndAdsEndedListeners will fire after the
     * post-roll completes.
     */
    this.contentAndAdsEndedListeners = [];

     /**
      * Listener to be called to trigger manual ad break playback.
      */
    this.adBreakReadyListener = undefined;

    /**
     * Stores the content source so we can re-populate it manually after a
     * post-roll on iOS.
     */
    this.contentSource = '';

    /**
     * Local content ended listener for contentComplete.
     */
    this.localContentEndedListener = function() {
      if (this.adsLoader && !this.contentComplete) {
        this.adsLoader.contentComplete();
        this.contentComplete = true;
      }
      for (var index in this.contentEndedListeners) {
        this.contentEndedListeners[index]();
      }
      if (this.allAdsCompleted) {
        for (var index in this.contentAndAdsEndedListeners) {
          this.contentAndAdsEndedListeners[index]();
        }
      }
      clearInterval(this.updateTimeIntervalHandle);
      clearInterval(this.seekCheckIntervalHandle);
      clearInterval(this.resizeCheckIntervalHandle);
      if(this.player.el()) {
        this.player.one('play', setUpPlayerIntervals_);
      }
    }.bind(this);

    this.playerDisposedListener = function(){
      this.contentEndedListeners, this.contentAndAdsEndedListeners = [], [];
      this.contentComplete = true;
      this.player.off('contentended', this.localContentEndedListener);
      this.player.off('timeupdate', updateLiveOffset_);

      // Bug fix: https://github.com/googleads/videojs-ima/issues/306
      if (this.player.ads.adTimeoutTimeout) {
        clearTimeout(this.player.ads.adTimeoutTimeout);
      }

      var intervalsToClear = [this.updateTimeIntervalHandle, this.seekCheckIntervalHandle,
        this.adTrackingTimer, this.resizeCheckIntervalHandle];
      for (var index in intervalsToClear) {
        var interval = intervalsToClear[index];
        if (interval) {
          clearInterval(interval);
        }
      }
      if (this.adsManager) {
        this.adsManager.destroy();
        this.adsManager = null;
      }
    }.bind(this);

    var offOnHandler = function(obj, target, event, off, on){
      if (target) {
        obj.off(target, event, off);
        obj.on(target, event, on);
      } else {
        obj.off(event, off);
        obj.on(event, on);
      }
    };

    this.uninitVjsControls = function() {
      if (this.overrides) {
        this.overrides.forEach(function(o){
          o.obj[o.method] = o.orig;
        });
      }
      if (this.overrideHandlers) {
        this.overrideHandlers.forEach(function(o){
          if (!o.obj.el_)
            return;
          offOnHandler(o.obj, o.target, o.event, o.handler, o.orig);
        });
      }
    }.bind(this);

    this.initVjsControls = function() {
      this.uninitVjsControls();
      var _this = this;
      this.overrides = [];
      var override = function(cls, obj, method, fn, always) {
        var orig = cls.prototype[method];
        _this.overrides.push({obj: obj, method: method, orig: orig});
        return obj[method] = function() {
          return _this.adsActive || always ? fn && fn.apply(this, arguments) :
            orig && orig.apply(this, arguments);
        };
      };
      this.overrideHandlers = [];
      var overrideHandler = function(cls, obj, target, event, method, fn, always) {
        var orig = cls.prototype[method];
        var handler = override(cls, obj, method, fn);
        _this.overrideHandlers.push({obj: obj, target: target, event: event,
          handler: handler, orig: orig});
        offOnHandler(obj, target, event, orig, handler);
      };
      var PlayToggle = videojs.getComponent('PlayToggle');
      var playToggle = this.vjsControls.playToggle;
      overrideHandler(PlayToggle, playToggle, null, ['tap', 'click'],
        'handleClick', function() {
        onAdPlayPauseClick_();
        if (_this.adPlaying) {
          this.handlePlay();
        } else {
          this.handlePause();
        }
      });
      override(PlayToggle, playToggle, 'update', function() {
        var paused = _this.adsActive ? !_this.adPlaying : player.paused();
        this.toggleClass('vjs-play-control-ad', _this.adsActive);
        this.toggleClass('vjs-paused', paused);
        this.toggleClass('vjs-playing', !paused);
        var text  = paused ? 'Play' : 'Pause';
        if (text != this.controlText())
          this.controlText(text);
      }, true);
      overrideHandler(PlayToggle, playToggle, player, 'play',
        'handlePlay', function() { this.update(); }, true);
      overrideHandler(PlayToggle, playToggle, player, 'pause',
        'handlePause', function() { this.update(); }, true);

      var SeekBar = videojs.getComponent('SeekBar');
      var DvrSeekBar = videojs.getComponent('DvrSeekBar');
      var seekBar = this.vjsControls.progressControl.seekBar;
      var getPercent = function() {
        var duration = _this.currentAd && _this.currentAd.getDuration();
        if (!duration || duration<0) {
          return 0;
        }
        var remainingTime = _this.adsManager.getRemainingTime();
        var currentTime = Math.max(duration - remainingTime, 0);
        return currentTime / duration;
      };
      override(SeekBar, seekBar, 'getPercent', getPercent);
      if (DvrSeekBar) {
        override(DvrSeekBar, seekBar, 'getPercent', getPercent);
      }
      overrideHandler(SeekBar, seekBar, null, ['mousedown', 'touchstart'],
        'handleMouseDown', null);
      overrideHandler(SeekBar, seekBar, null, 'focus', 'handleFocus', null);

      var DurationDisplay = videojs.getComponent('DurationDisplay');
      var durationDisplay = this.vjsControls.durationDisplay;
      overrideHandler(DurationDisplay, durationDisplay, player,
        ['timeupdate', 'loadedmetadata'], 'updateContent', function() {
        var duration = _this.currentAd && _this.currentAd.getDuration();
        if (duration && duration != this.duration_) {
          this.duration_  = duration;
          this.contentEl_.innerHTML = '<span class="vjs-control-text">'+
            this.localize('Duration Time')+'</span> '+formatTime(duration);
        }
      });

      var CurrentTimeDisplay = videojs.getComponent('CurrentTimeDisplay');
      var currentTimeDisplay = this.vjsControls.currentTimeDisplay;
      overrideHandler(CurrentTimeDisplay, currentTimeDisplay, player,
        ['timeupdate', 'loadedmetadata'], 'updateContent', function() {
        var duration = _this.currentAd && _this.currentAd.getDuration();
        if (!duration) {
          return;
        }
        var time = duration - _this.adsManager.getRemainingTime();
        var formattedTime = formatTime(time);
        if (formattedTime !== this.formattedTime_) {
          this.formattedTime_ = formattedTime;
          this.contentEl_.innerHTML = '<span class="vjs-control-text">'+
            this.localize('Current Time')+'</span> '+formattedTime;
        }
      });
    }.bind(this);

    /* Ad can be played in fullscreen mode on iOS only if custom playback
     * is enabled
     * https://ads-developers.googleblog.com/2017/06/new-custom-playback-apis-for-ima-html5.html
     */
    this.isFullscreenAdSupported = function() {
      return !videojs.browser.IS_IOS || this.adsManager.isCustomPlaybackUsed();
    }.bind(this);

    this.updateFullscreenButton = function() {
      this.player.toggleClass('vjs-disable-fullecreen', this.adsActive &&
       !this.isFullscreenAdSupported());
    }.bind(this);

    this.updateVjsControls = function() {
      if (!this.settings.vjsControls) {
        return;
      }
      this.player.toggleClass('vjs-ad-paused',
        this.adsActive && !this.adPlaying);
      var controls = this.vjsControls;
      controls.playToggle.update();
      controls.progressControl.seekBar.update();
      controls.durationDisplay.updateContent();
      controls.currentTimeDisplay.updateContent();
      var duration = this.currentAd && this.currentAd.getDuration();
      var display = !this.adsActive || duration && duration>=0 ? '' : 'none';
      controls.durationDisplay.el().style.display = display;
      controls.currentTimeDisplay.el().style.display = display;
      controls.timeDivider.el().style.display = display;
    }.bind(this);

    var getPosition = function(el) {
      var box = el.getBoundingClientRect();
      var docEl = document.documentElement;
      var body = document.body;
      var clientLeft = docEl.clientLeft || body.clientLeft || 0;
      var scrollLeft = window.pageXOffset || body.scrollLeft;
      var left = box.left + scrollLeft - clientLeft;
      var clientTop = docEl.clientTop || body.clientTop || 0;
      var scrollTop = window.pageYOffset || body.scrollTop;
      var top = box.top + scrollTop - clientTop;
      return {
        left: left,
        top: top,
        width: box.width,
        height: box.height,
      };
    };

    // proxy click events to the video element when non-linear ad is active
    this.proxyClickEvents = function() {
      var events = (videojs.browser.IS_ANDROID || videojs.browser.IS_IOS) ?
          ['touchstart', 'touchend'] :
          ['click', 'dblclick', 'mousedown', 'mouseup'];
      var player = this.player, el = player.el(), _this = this;
      events.forEach(function(eventName) {
        el.addEventListener(eventName, function(e) {
          var ad = _this.currentAd, t = e.target;
          if (!ad || ad.isLinear() || t.nodeName!='IFRAME' || e.isTrusted) {
            return;
          }
          // ignore clicks on ad ui elements
          var adWidth = ad.getWidth() || ad.getVastMediaWidth();
          var adHeight = ad.getHeight() || ad.getVastMediaHeight();
          var pos = getPosition(t);
          var touch = e.touches && e.touches[0];
          var x = touch ? touch.pageX : e.clientX;
          var y = touch ? touch.pageY : e.clientY;
          var adRight = pos.left+pos.width-(pos.width-adWidth)/2;
          var adTop = pos.top+pos.height-adHeight-4;
          // click on close button
          if (x<adRight && x>(adRight-40) && y>adTop && y<(adTop+30)) {
              return;
          }
          // click on recall button
          if (x>(pos.left+pos.width/2-15) && x<(pos.left+pos.width/2+15) &&
              y>(pos.top+pos.height-15)) {
              return;
          }
          var newEvent;
          var opt = {};
          for (var key in e) {
            opt[key] = e[key];
          }
          opt.bubbles = false;
          try {
            newEvent = new e.constructor(e.type, opt);
          } catch (err) {
            // special case for IE11
            newEvent = document.createEvent('MouseEvent');
            newEvent.initMouseEvent(e.type, opt.bubbles, opt.cancelable,
              opt.view, opt.detail, opt.screenX, opt.screenY, opt.clientX,
              opt.clientY, opt.ctrlKey, opt.altKey, opt.shiftKey, opt.metaKey,
              opt.button, null);
          }
          newEvent.stopPropagation();
          player.tech_.trigger(newEvent);
        });
      });
    }.bind(this);

    this.settings = extend({}, ima_defaults, options || {});
    this.settings.adLabel = this.player.localize(this.settings.adLabel);

    // Currently this isn't used but I can see it being needed in the future, so
    // to avoid implementation problems with later updates I'm requiring it.
    if (!this.settings['id']) {
      window.console.log('Error: must provide id of video.js div');
      return;
    }

    this.controlPrefix = (this.settings.id + '_') || '';

    this.contentPlayer = this.player.$('.vjs-tech');
    // Default showing countdown timer to true.
    this.showCountdown = true;
    if (this.settings['showCountdown'] == false) {
      this.showCountdown = false;
    }

    this.autoPlayAdBreaks = true;
    if (this.settings['autoPlayAdBreaks'] == false) {
      this.autoPlayAdBreaks = false;
    }

    var contrib_ads_defaults = {
      debug: this.settings.debug,
      timeout: this.settings.timeout,
      prerollTimeout: this.settings.prerollTimeout
    };

    var ads_plugin_settings =
        extend({}, contrib_ads_defaults, options['contribAdsSettings'] || {});

    player.ads(ads_plugin_settings);

    player.one('play', setUpPlayerIntervals_);
    player.on('contentended', this.localContentEndedListener);
    player.on('dispose', this.playerDisposedListener);
    player.on('timeupdate', updateLiveOffset_);
    this.adsRenderingSettings = new google.ima.AdsRenderingSettings();
    this.adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true;
    if (this.settings['adsRenderingSettings']) {
      for (var setting in this.settings['adsRenderingSettings']) {
        this.adsRenderingSettings[setting] =
            this.settings['adsRenderingSettings'][setting];
      }
    }

    if (this.settings['locale']) {
      google.ima.settings.setLocale(this.settings['locale']);
    }
    google.ima.settings.setDisableCustomPlaybackForIOS10Plus(
        !!this.settings.disableCustomPlaybackForIOS10Plus);

    createAdContainer_();
    this.adsLoader = new google.ima.AdsLoader(this.adDisplayContainer);

    this.adsLoader.getSettings().setVpaidMode(
        google.ima.ImaSdkSettings.VpaidMode.ENABLED);
    if (this.settings.vpaidAllowed == false) {
      this.adsLoader.getSettings().setVpaidMode(
          google.ima.ImaSdkSettings.VpaidMode.DISABLED);
    }
    if (this.settings.vpaidMode) {
      this.adsLoader.getSettings().setVpaidMode(this.settings.vpaidMode);
    }

    if (this.settings.locale) {
      this.adsLoader.getSettings().setLocale(this.settings.locale);
    }

    if (this.settings.numRedirects) {
      this.adsLoader.getSettings().setNumRedirects(this.settings.numRedirects);
    }

    this.adsLoader.getSettings().setPlayerType('videojs-ima');
    this.adsLoader.getSettings().setPlayerVersion(this.VERSION);
    this.adsLoader.getSettings().setAutoPlayAdBreaks(this.autoPlayAdBreaks);

    this.adsLoader.addEventListener(
      google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
      onAdsManagerLoaded_,
      false);
    this.adsLoader.addEventListener(
      google.ima.AdErrorEvent.Type.AD_ERROR,
      onAdsLoaderError_,
      false);

    if (!readyCallback) {
      readyCallback = this.startFromReadyCallback;
    }
    player.on('readyforpreroll', readyCallback);
    player.ready(function() {
      player.on('fullscreenchange', onFullscreenChange_);
      player.on('volumechange', onVolumeChange_);
    });
    this.proxyClickEvents();
  };

  function ExperimentalViewableImpression(options) {
    var MIN_VIEWABLE = 2; /* min number of seconds to report as viewable */
    this.conf = null;
    this.position = null;
    this.urls = [];
    this.opt = options.experimental_viewable_impression;
    this.enabled = this.opt && !this.opt.disable;

    this.initialize = function(adsResponse, callback) {
      if (!this.enabled) {
        return false;
      }
      try {
        return this._initialize(adsResponse, callback);
      } catch(err) {
        return false;
      }
    }.bind(this);

    this._initialize = function(adsResponse, callback) {
      var parser = new DOMParser();
      var adsXML = parser.parseFromString(adsResponse, 'text/xml');
      var adTags = adsXML.getElementsByTagName('vmap:AdTagURI');
      var vast4Tags = [];
      for (var i=0; i<adTags.length; i++) {
        if (/vast4/.test(adTags[i].getAttribute('templateType'))) {
          vast4Tags.push(adTags[i]);
        }
      }
      if (!vast4Tags.length) {
        return false;
      }
      var count = 0, anyChanged;
      for (var i=0; i<vast4Tags.length; i++) {
        this._requestAdTag(vast4Tags[i], i, function(changed) {
          anyChanged = anyChanged || changed;
          if (++count==vast4Tags.length) {
            if (!anyChanged) {
              callback();
              return;
            }
            var ret;
            try {
              var serializer = new XMLSerializer();
              ret = serializer.serializeToString(adsXML.documentElement);
            } catch(err) {}
            callback(ret);
          }
        });
      }
      return true;
    }.bind(this);

    this._checkAdUrl = function(url) {
      return !this.opt.ad_urls || this.opt.ad_urls.find(function(r) {
        return new RegExp(r).test(url);
      });
    }.bind(this);

    this._requestAdTag = function(tag, index, callback) {
      var adUrl = this._convertUrl(tag.textContent);
      if (!this._checkAdUrl(adUrl)) {
        callback(false);
        return;
      }

      videojs.xhr(adUrl, {withCredentials: true, timeout: 3000},
        function(err, response, body){

        if (err || response.statusCode!=200) {
          callback(false);
          return;
        }
        var ret = false;
        try {
          var xml = response.rawRequest.responseXML;
          // XXX: support fallbacks, use getWrapperAdIds from impression hook
          var ad = xml.getElementsByTagName('Ad')[0];
          if (ad) {
            var adUriTag = ad.getElementsByTagName('VASTAdTagURI')[0];
            var adUri;
            if (adUriTag && (adUri = this._convertUrl(adUriTag.textContent)) &&
                this._checkAdUrl(adUri)) {

              videojs.xhr(adUri, {withCredentials: true},
                  function(err, response, body) {

                  if (!err && response.statusCode==200) {
                    this._setAd(index, response.rawRequest.responseXML);
                  }
              }.bind(this));
            } else {
              this._setAd(index, ad);
            }
            var parent = tag.parentElement;
            parent.removeChild(tag);
            var adData = xml.createElement('vmap:VASTAdData');
            adData.appendChild(xml.documentElement);
            parent.appendChild(adData);
            ret = true;
          }
        } catch(err) {}
        callback(ret);
      }.bind(this));
    }.bind(this);

    this._convertUrl = function(url) {
      return encodeURI(url.trim()
        .replace(/\[referrer_url\]/i, window.location.href)
        .replace(/\[timestamp\]/i,
          encodeURIComponent(new Date().toISOString()))
        .replace(/\[[^\]]+\]/, function(x) { return encodeURIComponent(x); }));
    }.bind(this);

    this._setAd = function(index, ad) {
      var found = ad.getElementsByTagName('ViewableImpression')[0];
      if (!found)
        return;
      var urls = this.urls[index] = {};
      var get_url = function(key, tag_name){
        var tag = found.getElementsByTagName(tag_name)[0];
        if (tag) {
          urls[key] = tag.textContent.trim();
        }
      }.bind(this);
      get_url('viewable', 'Viewable');
      get_url('not_viewable', 'NotViewable');
      get_url('view_undetermined', 'ViewUndetermined');
    }.bind(this);

    this._makeReport = function(type) {
      var url = this.urls[this.index] && this.urls[this.index][type];
      if (url) {
        var img = new Image();
        img.src = this._convertUrl(url);
      }
    }.bind(this);

    this._isVisible = function(container) {
      var steps = 6;
      var rect = container.getBoundingClientRect();
      var limit = function(val, max){
        return Math.max(2, Math.min(val, max-2));
      };
      var total = 0, ok = 0;
      for (var i=0; i<=steps; i++) {
        for (var j=0; j<=steps; j++) {
          total++;
          var el = document.elementFromPoint(rect.left
              +limit(rect.width*i/steps, rect.width),
              rect.top+limit(rect.height*j/steps, rect.height));
          while (el && el!=container) {
            el = el.parentNode;
          }
          if (el==container) {
            ok++;
          }
        }
      }
      return ok/total>0.5;
    }.bind(this);

    this._onViewableImpression = function(evt) {
      var ad = evt.getAd(), id = ad.getAdId(), info = ad.getAdPodInfo();
      var index = info.getPodIndex(), position = info.getAdPosition();
      if (index==this.index && position==this.position) {
        this.position = null; // cancel manual detection
      }
    }.bind(this);

    this._onImpression = function(evt) {
      var ad = evt.getAd(), id = ad.getAdId(), info = ad.getAdPodInfo();
      this.index = info.getPodIndex();
      this.position = info.getAdPosition();
      this.conf = {pos: 0, total: 0, viewable: 0, reported: false,
          start: Date.now()};
    }.bind(this);

    this._onAdProgress = function(evt) {
      var data = evt.getAdData();
      if (!this.conf || this.position!=data.adPosition) {
        return;
      }
      var diff = data.currentTime-this.conf.pos;
      if (diff<=0) {
        return;
      }
      this.conf.pos = data.currentTime;
      this.conf.total += diff;
      try {
        if (this._isVisible(this.adContainer)) {
          this.conf.viewable += diff;
        }
      } catch(err) {
        this.conf.undetermined = true;
      }
      if (!this.conf.reported && this.conf.viewable>=MIN_VIEWABLE &&
          Date.now()-this.conf.start>MIN_VIEWABLE*1000) {
        this.conf.reported = true;
        this._makeReport('viewable');
      }
    }.bind(this);

    this._onComplete = function(evt) {
      var position = evt.getAd().getAdPodInfo().getAdPosition();
      if (!this.conf || this.position!=position) {
        return;
      }
      this.position = null;
      if (!this.conf.reported) {
        this._makeReport(this.conf.undetermined ? 'view_undetermined' :
            'not_viewable');
        this.conf = null;
      }
    }.bind(this);

    this.attach = function(adContainer, adsManager) {
      if (!this.enabled) {
        return;
      }
      this.adContainer = adContainer;
      adsManager.addEventListener(google.ima.AdEvent.Type.VIEWABLE_IMPRESSION,
          this._onViewableImpression);
      adsManager.addEventListener(google.ima.AdEvent.Type.IMPRESSION,
          this._onImpression);
      adsManager.addEventListener(google.ima.AdEvent.Type.AD_PROGRESS,
          this._onAdProgress);
      adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE,
          this._onComplete);
      adsManager.addEventListener(google.ima.AdEvent.Type.SKIPPED,
          this._onComplete);
    }.bind(this);
  };

  videojs.plugin('ima', init);
});


},{}],45:[function(_dereq_,module,exports){
(function(window, videojs, document, undefined){
'use strict';
/*jshint browser:true*/

var Flash = videojs.getComponent('Flash');

var Osmf = videojs.extend(Flash, {
    constructor: function(options, ready){
        var source = options.source;
        var _player = videojs(options.playerId);
        _player.osmf = this;
        options.flashVars = {
            'playerId': options.playerId,
            'readyFunction': 'onReady',
            'eventProxyFunction': 'onEvent',
            'errorEventProxyFunction': 'onError'
        };
        Flash.call(this, options, ready);
        this.firstplay = false;
        this.loadstart = false;
        _player.on('loadeddata', Osmf.onLoadedData);
        _player.on('ended', Osmf.onEnded);
        options.source = source;
    }
});

Osmf.formats = {
    'application/adobe-f4m': 'F4M',
    'application/adobe-f4v': 'F4V',
    'application/dash+xml': 'MPD'
};

Osmf.canPlaySource = function(src){
    var type = src.type.replace(/;.*/, '').toLowerCase();
    return type in Osmf.formats ? 'maybe' : '';
};

Osmf.log_enabled = false;

var api = Osmf.prototype;
var readWrite = ['preload', 'defaultPlaybackRate', 'playbackRate', 'autoplay',
    'loop', 'mediaGroup', 'controller', 'controls', 'volume', 'muted',
    'defaultMuted'];
var readOnly = ['error', 'networkState', 'readyState', 'seeking', 'videoWidth',
    'startOffsetTime', 'paused', 'played', 'ended', 'streamType',
    'initialTime', 'videoHeight', 'currentLevel', 'levels'];

var createSetter = function(attr){
    var attrUpper = attr.charAt(0).toUpperCase()+attr.slice(1);
    api['set'+attrUpper] = function(val){
        if (!this.el_.vjs_setProperty)
            return;
        return this.el_.vjs_setProperty(attr, val);
    };
};

var createGetter = function(attr){
    api[attr] = function(){
        if (!this.el_.vjs_getProperty)
            return;
        return this.el_.vjs_getProperty(attr);
    };
};

(function(){
    for (var i = 0; i<readWrite.length; i++)
    {
        createGetter(readWrite[i]);
        createSetter(readWrite[i]);
    }
    for (i = 0; i<readOnly.length; i++)
        createGetter(readOnly[i]);
})();

Osmf.prototype.duration = function(){
    return this.el_ ? (this.streamType() == 'live' ? Infinity :
        this.el_.vjs_getProperty('duration')) : 0;
};

Osmf.prototype.play = function(){ this.el_.vjs_play(); };

Osmf.prototype.load = function(){ this.el_.vjs_load(); };

Osmf.prototype.paused = function(){ return this.el_.vjs_paused(); };

Osmf.prototype.pause = function(){ this.el_.vjs_pause(); };

Osmf.prototype.currentTime = function(value){
    if (!value)
        return this.el_.vjs_getProperty('currentTime');
    this.el_.vjs_setProperty('currentTime');
};

Osmf.prototype.streamStatus = function(){ return this.el_.streamStatus(); };

Osmf.isSupported = function(){ return Flash.version()[0]>=10; };

Osmf.onLoadedData = function(){
    var player = this;
    if (player.options_.autoplay)
        player.play();
    else if (player.options_.preload && player.options_.preload!=='none')
    {
        if (player.currentTime())
            player.currentTime(0);
        player.play();
        player.pause();
        player.bigPlayButton.show();
        player.bigPlayButton.one('click', function(){
            player.bigPlayButton.hide(); });
    }
};

Osmf.onEnded = function(){
    if (this.options().loop)
        this.currentTime(0);
    this.pause();
};

Osmf.onReady = function(currentSwf){
    if (Osmf.log_enabled)
        videojs.log('OSMF', 'Ready', currentSwf);
    Flash.onReady(currentSwf);
    var tech = document.getElementById(currentSwf).tech;
    if (tech.currentSrc() && tech.currentSrc().length>0)
        tech.el_.vjs_src(tech.currentSrc());
};

Osmf.onError = function(currentSwf, err){
    var tech = document.getElementById(currentSwf).tech;
    if (err=='loaderror')
        err = 'srcnotfound';
    if (Osmf.log_enabled)
        videojs.log('OSMF', 'Error', err);
    if (tech.options_.reconnectOnError && !tech.reconnecting_)
    {
        tech.reconnecting_ = true;
        tech.trigger("waiting");
        setTimeout(function(){
            tech.src(tech.currentSrc());
            tech.reconnecting_ = false;
            tech.error(null);
        }, 5000);
    }
    tech.error({code: 4, msg: ""});
};

Osmf.onEvent = function(currentSwf, event, data){
    var tech = document.getElementById(currentSwf).tech;
    switch (event)
    {
    case 'playing':
        if (tech.firstplay===false)
        {
            if (Osmf.log_enabled)
                videojs.log('OSMF', 'Event', currentSwf, 'loadstart');
            tech.trigger('loadstart');
            tech.loadstart = true;
            if (Osmf.log_enabled)
                videojs.log('OSMF', 'Event', currentSwf, 'firstplay');
            tech.trigger('firstplay');
            tech.firstplay = true;
        }
        break;
    case 'buffering':
        event = 'waiting';
        break;
    case 'ready':
        event = 'loadeddata';
        break;
    }
    tech.trigger(event, data);
    if (event!=='timeupdate' && Osmf.log_enabled)
        videojs.log('OSMF', 'Event', currentSwf, event);
};

Osmf.prototype.supportsFullScreen = function(){ return false; };

Osmf.prototype.enterFullScreen = function(){ return false; };

videojs.options.osmf = {};
videojs.options.techOrder.push('osmf');
videojs.registerComponent('Osmf', Osmf);

})(window, window.videojs, document);

},{}],46:[function(_dereq_,module,exports){
var getNative = _dereq_('./_getNative'),
    root = _dereq_('./_root');

/* Built-in method references that are verified to be native. */
var DataView = getNative(root, 'DataView');

module.exports = DataView;

},{"./_getNative":95,"./_root":130}],47:[function(_dereq_,module,exports){
var hashClear = _dereq_('./_hashClear'),
    hashDelete = _dereq_('./_hashDelete'),
    hashGet = _dereq_('./_hashGet'),
    hashHas = _dereq_('./_hashHas'),
    hashSet = _dereq_('./_hashSet');

/**
 * Creates a hash object.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function Hash(entries) {
  var index = -1,
      length = entries == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

// Add methods to `Hash`.
Hash.prototype.clear = hashClear;
Hash.prototype['delete'] = hashDelete;
Hash.prototype.get = hashGet;
Hash.prototype.has = hashHas;
Hash.prototype.set = hashSet;

module.exports = Hash;

},{"./_hashClear":101,"./_hashDelete":102,"./_hashGet":103,"./_hashHas":104,"./_hashSet":105}],48:[function(_dereq_,module,exports){
var listCacheClear = _dereq_('./_listCacheClear'),
    listCacheDelete = _dereq_('./_listCacheDelete'),
    listCacheGet = _dereq_('./_listCacheGet'),
    listCacheHas = _dereq_('./_listCacheHas'),
    listCacheSet = _dereq_('./_listCacheSet');

/**
 * Creates an list cache object.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function ListCache(entries) {
  var index = -1,
      length = entries == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

// Add methods to `ListCache`.
ListCache.prototype.clear = listCacheClear;
ListCache.prototype['delete'] = listCacheDelete;
ListCache.prototype.get = listCacheGet;
ListCache.prototype.has = listCacheHas;
ListCache.prototype.set = listCacheSet;

module.exports = ListCache;

},{"./_listCacheClear":112,"./_listCacheDelete":113,"./_listCacheGet":114,"./_listCacheHas":115,"./_listCacheSet":116}],49:[function(_dereq_,module,exports){
var getNative = _dereq_('./_getNative'),
    root = _dereq_('./_root');

/* Built-in method references that are verified to be native. */
var Map = getNative(root, 'Map');

module.exports = Map;

},{"./_getNative":95,"./_root":130}],50:[function(_dereq_,module,exports){
var mapCacheClear = _dereq_('./_mapCacheClear'),
    mapCacheDelete = _dereq_('./_mapCacheDelete'),
    mapCacheGet = _dereq_('./_mapCacheGet'),
    mapCacheHas = _dereq_('./_mapCacheHas'),
    mapCacheSet = _dereq_('./_mapCacheSet');

/**
 * Creates a map cache object to store key-value pairs.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function MapCache(entries) {
  var index = -1,
      length = entries == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

// Add methods to `MapCache`.
MapCache.prototype.clear = mapCacheClear;
MapCache.prototype['delete'] = mapCacheDelete;
MapCache.prototype.get = mapCacheGet;
MapCache.prototype.has = mapCacheHas;
MapCache.prototype.set = mapCacheSet;

module.exports = MapCache;

},{"./_mapCacheClear":117,"./_mapCacheDelete":118,"./_mapCacheGet":119,"./_mapCacheHas":120,"./_mapCacheSet":121}],51:[function(_dereq_,module,exports){
var getNative = _dereq_('./_getNative'),
    root = _dereq_('./_root');

/* Built-in method references that are verified to be native. */
var Promise = getNative(root, 'Promise');

module.exports = Promise;

},{"./_getNative":95,"./_root":130}],52:[function(_dereq_,module,exports){
var getNative = _dereq_('./_getNative'),
    root = _dereq_('./_root');

/* Built-in method references that are verified to be native. */
var Set = getNative(root, 'Set');

module.exports = Set;

},{"./_getNative":95,"./_root":130}],53:[function(_dereq_,module,exports){
var MapCache = _dereq_('./_MapCache'),
    setCacheAdd = _dereq_('./_setCacheAdd'),
    setCacheHas = _dereq_('./_setCacheHas');

/**
 *
 * Creates an array cache object to store unique values.
 *
 * @private
 * @constructor
 * @param {Array} [values] The values to cache.
 */
function SetCache(values) {
  var index = -1,
      length = values == null ? 0 : values.length;

  this.__data__ = new MapCache;
  while (++index < length) {
    this.add(values[index]);
  }
}

// Add methods to `SetCache`.
SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
SetCache.prototype.has = setCacheHas;

module.exports = SetCache;

},{"./_MapCache":50,"./_setCacheAdd":131,"./_setCacheHas":132}],54:[function(_dereq_,module,exports){
var ListCache = _dereq_('./_ListCache'),
    stackClear = _dereq_('./_stackClear'),
    stackDelete = _dereq_('./_stackDelete'),
    stackGet = _dereq_('./_stackGet'),
    stackHas = _dereq_('./_stackHas'),
    stackSet = _dereq_('./_stackSet');

/**
 * Creates a stack cache object to store key-value pairs.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function Stack(entries) {
  var data = this.__data__ = new ListCache(entries);
  this.size = data.size;
}

// Add methods to `Stack`.
Stack.prototype.clear = stackClear;
Stack.prototype['delete'] = stackDelete;
Stack.prototype.get = stackGet;
Stack.prototype.has = stackHas;
Stack.prototype.set = stackSet;

module.exports = Stack;

},{"./_ListCache":48,"./_stackClear":134,"./_stackDelete":135,"./_stackGet":136,"./_stackHas":137,"./_stackSet":138}],55:[function(_dereq_,module,exports){
var root = _dereq_('./_root');

/** Built-in value references. */
var Symbol = root.Symbol;

module.exports = Symbol;

},{"./_root":130}],56:[function(_dereq_,module,exports){
var root = _dereq_('./_root');

/** Built-in value references. */
var Uint8Array = root.Uint8Array;

module.exports = Uint8Array;

},{"./_root":130}],57:[function(_dereq_,module,exports){
var getNative = _dereq_('./_getNative'),
    root = _dereq_('./_root');

/* Built-in method references that are verified to be native. */
var WeakMap = getNative(root, 'WeakMap');

module.exports = WeakMap;

},{"./_getNative":95,"./_root":130}],58:[function(_dereq_,module,exports){
/**
 * A specialized version of `_.filter` for arrays without support for
 * iteratee shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} predicate The function invoked per iteration.
 * @returns {Array} Returns the new filtered array.
 */
function arrayFilter(array, predicate) {
  var index = -1,
      length = array == null ? 0 : array.length,
      resIndex = 0,
      result = [];

  while (++index < length) {
    var value = array[index];
    if (predicate(value, index, array)) {
      result[resIndex++] = value;
    }
  }
  return result;
}

module.exports = arrayFilter;

},{}],59:[function(_dereq_,module,exports){
var baseTimes = _dereq_('./_baseTimes'),
    isArguments = _dereq_('./isArguments'),
    isArray = _dereq_('./isArray'),
    isBuffer = _dereq_('./isBuffer'),
    isIndex = _dereq_('./_isIndex'),
    isTypedArray = _dereq_('./isTypedArray');

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Creates an array of the enumerable property names of the array-like `value`.
 *
 * @private
 * @param {*} value The value to query.
 * @param {boolean} inherited Specify returning inherited property names.
 * @returns {Array} Returns the array of property names.
 */
function arrayLikeKeys(value, inherited) {
  var isArr = isArray(value),
      isArg = !isArr && isArguments(value),
      isBuff = !isArr && !isArg && isBuffer(value),
      isType = !isArr && !isArg && !isBuff && isTypedArray(value),
      skipIndexes = isArr || isArg || isBuff || isType,
      result = skipIndexes ? baseTimes(value.length, String) : [],
      length = result.length;

  for (var key in value) {
    if ((inherited || hasOwnProperty.call(value, key)) &&
        !(skipIndexes && (
           // Safari 9 has enumerable `arguments.length` in strict mode.
           key == 'length' ||
           // Node.js 0.10 has enumerable non-index properties on buffers.
           (isBuff && (key == 'offset' || key == 'parent')) ||
           // PhantomJS 2 has enumerable non-index properties on typed arrays.
           (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
           // Skip index properties.
           isIndex(key, length)
        ))) {
      result.push(key);
    }
  }
  return result;
}

module.exports = arrayLikeKeys;

},{"./_baseTimes":81,"./_isIndex":106,"./isArguments":148,"./isArray":149,"./isBuffer":151,"./isTypedArray":157}],60:[function(_dereq_,module,exports){
/**
 * A specialized version of `_.map` for arrays without support for iteratee
 * shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns the new mapped array.
 */
function arrayMap(array, iteratee) {
  var index = -1,
      length = array == null ? 0 : array.length,
      result = Array(length);

  while (++index < length) {
    result[index] = iteratee(array[index], index, array);
  }
  return result;
}

module.exports = arrayMap;

},{}],61:[function(_dereq_,module,exports){
/**
 * Appends the elements of `values` to `array`.
 *
 * @private
 * @param {Array} array The array to modify.
 * @param {Array} values The values to append.
 * @returns {Array} Returns `array`.
 */
function arrayPush(array, values) {
  var index = -1,
      length = values.length,
      offset = array.length;

  while (++index < length) {
    array[offset + index] = values[index];
  }
  return array;
}

module.exports = arrayPush;

},{}],62:[function(_dereq_,module,exports){
/**
 * A specialized version of `_.some` for arrays without support for iteratee
 * shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} predicate The function invoked per iteration.
 * @returns {boolean} Returns `true` if any element passes the predicate check,
 *  else `false`.
 */
function arraySome(array, predicate) {
  var index = -1,
      length = array == null ? 0 : array.length;

  while (++index < length) {
    if (predicate(array[index], index, array)) {
      return true;
    }
  }
  return false;
}

module.exports = arraySome;

},{}],63:[function(_dereq_,module,exports){
var eq = _dereq_('./eq');

/**
 * Gets the index at which the `key` is found in `array` of key-value pairs.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} key The key to search for.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function assocIndexOf(array, key) {
  var length = array.length;
  while (length--) {
    if (eq(array[length][0], key)) {
      return length;
    }
  }
  return -1;
}

module.exports = assocIndexOf;

},{"./eq":142}],64:[function(_dereq_,module,exports){
/**
 * The base implementation of `_.findIndex` and `_.findLastIndex` without
 * support for iteratee shorthands.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {Function} predicate The function invoked per iteration.
 * @param {number} fromIndex The index to search from.
 * @param {boolean} [fromRight] Specify iterating from right to left.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function baseFindIndex(array, predicate, fromIndex, fromRight) {
  var length = array.length,
      index = fromIndex + (fromRight ? 1 : -1);

  while ((fromRight ? index-- : ++index < length)) {
    if (predicate(array[index], index, array)) {
      return index;
    }
  }
  return -1;
}

module.exports = baseFindIndex;

},{}],65:[function(_dereq_,module,exports){
var castPath = _dereq_('./_castPath'),
    toKey = _dereq_('./_toKey');

/**
 * The base implementation of `_.get` without support for default values.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {Array|string} path The path of the property to get.
 * @returns {*} Returns the resolved value.
 */
function baseGet(object, path) {
  path = castPath(path, object);

  var index = 0,
      length = path.length;

  while (object != null && index < length) {
    object = object[toKey(path[index++])];
  }
  return (index && index == length) ? object : undefined;
}

module.exports = baseGet;

},{"./_castPath":85,"./_toKey":140}],66:[function(_dereq_,module,exports){
var arrayPush = _dereq_('./_arrayPush'),
    isArray = _dereq_('./isArray');

/**
 * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
 * `keysFunc` and `symbolsFunc` to get the enumerable property names and
 * symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {Function} keysFunc The function to get the keys of `object`.
 * @param {Function} symbolsFunc The function to get the symbols of `object`.
 * @returns {Array} Returns the array of property names and symbols.
 */
function baseGetAllKeys(object, keysFunc, symbolsFunc) {
  var result = keysFunc(object);
  return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
}

module.exports = baseGetAllKeys;

},{"./_arrayPush":61,"./isArray":149}],67:[function(_dereq_,module,exports){
var Symbol = _dereq_('./_Symbol'),
    getRawTag = _dereq_('./_getRawTag'),
    objectToString = _dereq_('./_objectToString');

/** `Object#toString` result references. */
var nullTag = '[object Null]',
    undefinedTag = '[object Undefined]';

/** Built-in value references. */
var symToStringTag = Symbol ? Symbol.toStringTag : undefined;

/**
 * The base implementation of `getTag` without fallbacks for buggy environments.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
function baseGetTag(value) {
  if (value == null) {
    return value === undefined ? undefinedTag : nullTag;
  }
  return (symToStringTag && symToStringTag in Object(value))
    ? getRawTag(value)
    : objectToString(value);
}

module.exports = baseGetTag;

},{"./_Symbol":55,"./_getRawTag":96,"./_objectToString":128}],68:[function(_dereq_,module,exports){
/**
 * The base implementation of `_.hasIn` without support for deep paths.
 *
 * @private
 * @param {Object} [object] The object to query.
 * @param {Array|string} key The key to check.
 * @returns {boolean} Returns `true` if `key` exists, else `false`.
 */
function baseHasIn(object, key) {
  return object != null && key in Object(object);
}

module.exports = baseHasIn;

},{}],69:[function(_dereq_,module,exports){
var baseGetTag = _dereq_('./_baseGetTag'),
    isObjectLike = _dereq_('./isObjectLike');

/** `Object#toString` result references. */
var argsTag = '[object Arguments]';

/**
 * The base implementation of `_.isArguments`.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
 */
function baseIsArguments(value) {
  return isObjectLike(value) && baseGetTag(value) == argsTag;
}

module.exports = baseIsArguments;

},{"./_baseGetTag":67,"./isObjectLike":155}],70:[function(_dereq_,module,exports){
var baseIsEqualDeep = _dereq_('./_baseIsEqualDeep'),
    isObjectLike = _dereq_('./isObjectLike');

/**
 * The base implementation of `_.isEqual` which supports partial comparisons
 * and tracks traversed objects.
 *
 * @private
 * @param {*} value The value to compare.
 * @param {*} other The other value to compare.
 * @param {boolean} bitmask The bitmask flags.
 *  1 - Unordered comparison
 *  2 - Partial comparison
 * @param {Function} [customizer] The function to customize comparisons.
 * @param {Object} [stack] Tracks traversed `value` and `other` objects.
 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
 */
function baseIsEqual(value, other, bitmask, customizer, stack) {
  if (value === other) {
    return true;
  }
  if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {
    return value !== value && other !== other;
  }
  return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
}

module.exports = baseIsEqual;

},{"./_baseIsEqualDeep":71,"./isObjectLike":155}],71:[function(_dereq_,module,exports){
var Stack = _dereq_('./_Stack'),
    equalArrays = _dereq_('./_equalArrays'),
    equalByTag = _dereq_('./_equalByTag'),
    equalObjects = _dereq_('./_equalObjects'),
    getTag = _dereq_('./_getTag'),
    isArray = _dereq_('./isArray'),
    isBuffer = _dereq_('./isBuffer'),
    isTypedArray = _dereq_('./isTypedArray');

/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1;

/** `Object#toString` result references. */
var argsTag = '[object Arguments]',
    arrayTag = '[object Array]',
    objectTag = '[object Object]';

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * A specialized version of `baseIsEqual` for arrays and objects which performs
 * deep comparisons and tracks traversed objects enabling objects with circular
 * references to be compared.
 *
 * @private
 * @param {Object} object The object to compare.
 * @param {Object} other The other object to compare.
 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} [stack] Tracks traversed `object` and `other` objects.
 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
 */
function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
  var objIsArr = isArray(object),
      othIsArr = isArray(other),
      objTag = objIsArr ? arrayTag : getTag(object),
      othTag = othIsArr ? arrayTag : getTag(other);

  objTag = objTag == argsTag ? objectTag : objTag;
  othTag = othTag == argsTag ? objectTag : othTag;

  var objIsObj = objTag == objectTag,
      othIsObj = othTag == objectTag,
      isSameTag = objTag == othTag;

  if (isSameTag && isBuffer(object)) {
    if (!isBuffer(other)) {
      return false;
    }
    objIsArr = true;
    objIsObj = false;
  }
  if (isSameTag && !objIsObj) {
    stack || (stack = new Stack);
    return (objIsArr || isTypedArray(object))
      ? equalArrays(object, other, bitmask, customizer, equalFunc, stack)
      : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
  }
  if (!(bitmask & COMPARE_PARTIAL_FLAG)) {
    var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
        othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');

    if (objIsWrapped || othIsWrapped) {
      var objUnwrapped = objIsWrapped ? object.value() : object,
          othUnwrapped = othIsWrapped ? other.value() : other;

      stack || (stack = new Stack);
      return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);
    }
  }
  if (!isSameTag) {
    return false;
  }
  stack || (stack = new Stack);
  return equalObjects(object, other, bitmask, customizer, equalFunc, stack);
}

module.exports = baseIsEqualDeep;

},{"./_Stack":54,"./_equalArrays":88,"./_equalByTag":89,"./_equalObjects":90,"./_getTag":98,"./isArray":149,"./isBuffer":151,"./isTypedArray":157}],72:[function(_dereq_,module,exports){
var Stack = _dereq_('./_Stack'),
    baseIsEqual = _dereq_('./_baseIsEqual');

/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1,
    COMPARE_UNORDERED_FLAG = 2;

/**
 * The base implementation of `_.isMatch` without support for iteratee shorthands.
 *
 * @private
 * @param {Object} object The object to inspect.
 * @param {Object} source The object of property values to match.
 * @param {Array} matchData The property names, values, and compare flags to match.
 * @param {Function} [customizer] The function to customize comparisons.
 * @returns {boolean} Returns `true` if `object` is a match, else `false`.
 */
function baseIsMatch(object, source, matchData, customizer) {
  var index = matchData.length,
      length = index,
      noCustomizer = !customizer;

  if (object == null) {
    return !length;
  }
  object = Object(object);
  while (index--) {
    var data = matchData[index];
    if ((noCustomizer && data[2])
          ? data[1] !== object[data[0]]
          : !(data[0] in object)
        ) {
      return false;
    }
  }
  while (++index < length) {
    data = matchData[index];
    var key = data[0],
        objValue = object[key],
        srcValue = data[1];

    if (noCustomizer && data[2]) {
      if (objValue === undefined && !(key in object)) {
        return false;
      }
    } else {
      var stack = new Stack;
      if (customizer) {
        var result = customizer(objValue, srcValue, key, object, source, stack);
      }
      if (!(result === undefined
            ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack)
            : result
          )) {
        return false;
      }
    }
  }
  return true;
}

module.exports = baseIsMatch;

},{"./_Stack":54,"./_baseIsEqual":70}],73:[function(_dereq_,module,exports){
var isFunction = _dereq_('./isFunction'),
    isMasked = _dereq_('./_isMasked'),
    isObject = _dereq_('./isObject'),
    toSource = _dereq_('./_toSource');

/**
 * Used to match `RegExp`
 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
 */
var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;

/** Used to detect host constructors (Safari). */
var reIsHostCtor = /^\[object .+?Constructor\]$/;

/** Used for built-in method references. */
var funcProto = Function.prototype,
    objectProto = Object.prototype;

/** Used to resolve the decompiled source of functions. */
var funcToString = funcProto.toString;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/** Used to detect if a method is native. */
var reIsNative = RegExp('^' +
  funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
  .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
);

/**
 * The base implementation of `_.isNative` without bad shim checks.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a native function,
 *  else `false`.
 */
function baseIsNative(value) {
  if (!isObject(value) || isMasked(value)) {
    return false;
  }
  var pattern = isFunction(value) ? reIsNative : reIsHostCtor;
  return pattern.test(toSource(value));
}

module.exports = baseIsNative;

},{"./_isMasked":109,"./_toSource":141,"./isFunction":152,"./isObject":154}],74:[function(_dereq_,module,exports){
var baseGetTag = _dereq_('./_baseGetTag'),
    isLength = _dereq_('./isLength'),
    isObjectLike = _dereq_('./isObjectLike');

/** `Object#toString` result references. */
var argsTag = '[object Arguments]',
    arrayTag = '[object Array]',
    boolTag = '[object Boolean]',
    dateTag = '[object Date]',
    errorTag = '[object Error]',
    funcTag = '[object Function]',
    mapTag = '[object Map]',
    numberTag = '[object Number]',
    objectTag = '[object Object]',
    regexpTag = '[object RegExp]',
    setTag = '[object Set]',
    stringTag = '[object String]',
    weakMapTag = '[object WeakMap]';

var arrayBufferTag = '[object ArrayBuffer]',
    dataViewTag = '[object DataView]',
    float32Tag = '[object Float32Array]',
    float64Tag = '[object Float64Array]',
    int8Tag = '[object Int8Array]',
    int16Tag = '[object Int16Array]',
    int32Tag = '[object Int32Array]',
    uint8Tag = '[object Uint8Array]',
    uint8ClampedTag = '[object Uint8ClampedArray]',
    uint16Tag = '[object Uint16Array]',
    uint32Tag = '[object Uint32Array]';

/** Used to identify `toStringTag` values of typed arrays. */
var typedArrayTags = {};
typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
typedArrayTags[uint32Tag] = true;
typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
typedArrayTags[errorTag] = typedArrayTags[funcTag] =
typedArrayTags[mapTag] = typedArrayTags[numberTag] =
typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
typedArrayTags[setTag] = typedArrayTags[stringTag] =
typedArrayTags[weakMapTag] = false;

/**
 * The base implementation of `_.isTypedArray` without Node.js optimizations.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
 */
function baseIsTypedArray(value) {
  return isObjectLike(value) &&
    isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
}

module.exports = baseIsTypedArray;

},{"./_baseGetTag":67,"./isLength":153,"./isObjectLike":155}],75:[function(_dereq_,module,exports){
var baseMatches = _dereq_('./_baseMatches'),
    baseMatchesProperty = _dereq_('./_baseMatchesProperty'),
    identity = _dereq_('./identity'),
    isArray = _dereq_('./isArray'),
    property = _dereq_('./property');

/**
 * The base implementation of `_.iteratee`.
 *
 * @private
 * @param {*} [value=_.identity] The value to convert to an iteratee.
 * @returns {Function} Returns the iteratee.
 */
function baseIteratee(value) {
  // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
  // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
  if (typeof value == 'function') {
    return value;
  }
  if (value == null) {
    return identity;
  }
  if (typeof value == 'object') {
    return isArray(value)
      ? baseMatchesProperty(value[0], value[1])
      : baseMatches(value);
  }
  return property(value);
}

module.exports = baseIteratee;

},{"./_baseMatches":77,"./_baseMatchesProperty":78,"./identity":147,"./isArray":149,"./property":160}],76:[function(_dereq_,module,exports){
var isPrototype = _dereq_('./_isPrototype'),
    nativeKeys = _dereq_('./_nativeKeys');

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 */
function baseKeys(object) {
  if (!isPrototype(object)) {
    return nativeKeys(object);
  }
  var result = [];
  for (var key in Object(object)) {
    if (hasOwnProperty.call(object, key) && key != 'constructor') {
      result.push(key);
    }
  }
  return result;
}

module.exports = baseKeys;

},{"./_isPrototype":110,"./_nativeKeys":126}],77:[function(_dereq_,module,exports){
var baseIsMatch = _dereq_('./_baseIsMatch'),
    getMatchData = _dereq_('./_getMatchData'),
    matchesStrictComparable = _dereq_('./_matchesStrictComparable');

/**
 * The base implementation of `_.matches` which doesn't clone `source`.
 *
 * @private
 * @param {Object} source The object of property values to match.
 * @returns {Function} Returns the new spec function.
 */
function baseMatches(source) {
  var matchData = getMatchData(source);
  if (matchData.length == 1 && matchData[0][2]) {
    return matchesStrictComparable(matchData[0][0], matchData[0][1]);
  }
  return function(object) {
    return object === source || baseIsMatch(object, source, matchData);
  };
}

module.exports = baseMatches;

},{"./_baseIsMatch":72,"./_getMatchData":94,"./_matchesStrictComparable":123}],78:[function(_dereq_,module,exports){
var baseIsEqual = _dereq_('./_baseIsEqual'),
    get = _dereq_('./get'),
    hasIn = _dereq_('./hasIn'),
    isKey = _dereq_('./_isKey'),
    isStrictComparable = _dereq_('./_isStrictComparable'),
    matchesStrictComparable = _dereq_('./_matchesStrictComparable'),
    toKey = _dereq_('./_toKey');

/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1,
    COMPARE_UNORDERED_FLAG = 2;

/**
 * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.
 *
 * @private
 * @param {string} path The path of the property to get.
 * @param {*} srcValue The value to match.
 * @returns {Function} Returns the new spec function.
 */
function baseMatchesProperty(path, srcValue) {
  if (isKey(path) && isStrictComparable(srcValue)) {
    return matchesStrictComparable(toKey(path), srcValue);
  }
  return function(object) {
    var objValue = get(object, path);
    return (objValue === undefined && objValue === srcValue)
      ? hasIn(object, path)
      : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
  };
}

module.exports = baseMatchesProperty;

},{"./_baseIsEqual":70,"./_isKey":107,"./_isStrictComparable":111,"./_matchesStrictComparable":123,"./_toKey":140,"./get":145,"./hasIn":146}],79:[function(_dereq_,module,exports){
/**
 * The base implementation of `_.property` without support for deep paths.
 *
 * @private
 * @param {string} key The key of the property to get.
 * @returns {Function} Returns the new accessor function.
 */
function baseProperty(key) {
  return function(object) {
    return object == null ? undefined : object[key];
  };
}

module.exports = baseProperty;

},{}],80:[function(_dereq_,module,exports){
var baseGet = _dereq_('./_baseGet');

/**
 * A specialized version of `baseProperty` which supports deep paths.
 *
 * @private
 * @param {Array|string} path The path of the property to get.
 * @returns {Function} Returns the new accessor function.
 */
function basePropertyDeep(path) {
  return function(object) {
    return baseGet(object, path);
  };
}

module.exports = basePropertyDeep;

},{"./_baseGet":65}],81:[function(_dereq_,module,exports){
/**
 * The base implementation of `_.times` without support for iteratee shorthands
 * or max array length checks.
 *
 * @private
 * @param {number} n The number of times to invoke `iteratee`.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns the array of results.
 */
function baseTimes(n, iteratee) {
  var index = -1,
      result = Array(n);

  while (++index < n) {
    result[index] = iteratee(index);
  }
  return result;
}

module.exports = baseTimes;

},{}],82:[function(_dereq_,module,exports){
var Symbol = _dereq_('./_Symbol'),
    arrayMap = _dereq_('./_arrayMap'),
    isArray = _dereq_('./isArray'),
    isSymbol = _dereq_('./isSymbol');

/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0;

/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol ? Symbol.prototype : undefined,
    symbolToString = symbolProto ? symbolProto.toString : undefined;

/**
 * The base implementation of `_.toString` which doesn't convert nullish
 * values to empty strings.
 *
 * @private
 * @param {*} value The value to process.
 * @returns {string} Returns the string.
 */
function baseToString(value) {
  // Exit early for strings to avoid a performance hit in some environments.
  if (typeof value == 'string') {
    return value;
  }
  if (isArray(value)) {
    // Recursively convert values (susceptible to call stack limits).
    return arrayMap(value, baseToString) + '';
  }
  if (isSymbol(value)) {
    return symbolToString ? symbolToString.call(value) : '';
  }
  var result = (value + '');
  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
}

module.exports = baseToString;

},{"./_Symbol":55,"./_arrayMap":60,"./isArray":149,"./isSymbol":156}],83:[function(_dereq_,module,exports){
/**
 * The base implementation of `_.unary` without support for storing metadata.
 *
 * @private
 * @param {Function} func The function to cap arguments for.
 * @returns {Function} Returns the new capped function.
 */
function baseUnary(func) {
  return function(value) {
    return func(value);
  };
}

module.exports = baseUnary;

},{}],84:[function(_dereq_,module,exports){
/**
 * Checks if a `cache` value for `key` exists.
 *
 * @private
 * @param {Object} cache The cache to query.
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function cacheHas(cache, key) {
  return cache.has(key);
}

module.exports = cacheHas;

},{}],85:[function(_dereq_,module,exports){
var isArray = _dereq_('./isArray'),
    isKey = _dereq_('./_isKey'),
    stringToPath = _dereq_('./_stringToPath'),
    toString = _dereq_('./toString');

/**
 * Casts `value` to a path array if it's not one.
 *
 * @private
 * @param {*} value The value to inspect.
 * @param {Object} [object] The object to query keys on.
 * @returns {Array} Returns the cast property path array.
 */
function castPath(value, object) {
  if (isArray(value)) {
    return value;
  }
  return isKey(value, object) ? [value] : stringToPath(toString(value));
}

module.exports = castPath;

},{"./_isKey":107,"./_stringToPath":139,"./isArray":149,"./toString":166}],86:[function(_dereq_,module,exports){
var root = _dereq_('./_root');

/** Used to detect overreaching core-js shims. */
var coreJsData = root['__core-js_shared__'];

module.exports = coreJsData;

},{"./_root":130}],87:[function(_dereq_,module,exports){
var baseIteratee = _dereq_('./_baseIteratee'),
    isArrayLike = _dereq_('./isArrayLike'),
    keys = _dereq_('./keys');

/**
 * Creates a `_.find` or `_.findLast` function.
 *
 * @private
 * @param {Function} findIndexFunc The function to find the collection index.
 * @returns {Function} Returns the new find function.
 */
function createFind(findIndexFunc) {
  return function(collection, predicate, fromIndex) {
    var iterable = Object(collection);
    if (!isArrayLike(collection)) {
      var iteratee = baseIteratee(predicate, 3);
      collection = keys(collection);
      predicate = function(key) { return iteratee(iterable[key], key, iterable); };
    }
    var index = findIndexFunc(collection, predicate, fromIndex);
    return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;
  };
}

module.exports = createFind;

},{"./_baseIteratee":75,"./isArrayLike":150,"./keys":158}],88:[function(_dereq_,module,exports){
var SetCache = _dereq_('./_SetCache'),
    arraySome = _dereq_('./_arraySome'),
    cacheHas = _dereq_('./_cacheHas');

/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1,
    COMPARE_UNORDERED_FLAG = 2;

/**
 * A specialized version of `baseIsEqualDeep` for arrays with support for
 * partial deep comparisons.
 *
 * @private
 * @param {Array} array The array to compare.
 * @param {Array} other The other array to compare.
 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} stack Tracks traversed `array` and `other` objects.
 * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
 */
function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
  var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
      arrLength = array.length,
      othLength = other.length;

  if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
    return false;
  }
  // Assume cyclic values are equal.
  var stacked = stack.get(array);
  if (stacked && stack.get(other)) {
    return stacked == other;
  }
  var index = -1,
      result = true,
      seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined;

  stack.set(array, other);
  stack.set(other, array);

  // Ignore non-index properties.
  while (++index < arrLength) {
    var arrValue = array[index],
        othValue = other[index];

    if (customizer) {
      var compared = isPartial
        ? customizer(othValue, arrValue, index, other, array, stack)
        : customizer(arrValue, othValue, index, array, other, stack);
    }
    if (compared !== undefined) {
      if (compared) {
        continue;
      }
      result = false;
      break;
    }
    // Recursively compare arrays (susceptible to call stack limits).
    if (seen) {
      if (!arraySome(other, function(othValue, othIndex) {
            if (!cacheHas(seen, othIndex) &&
                (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
              return seen.push(othIndex);
            }
          })) {
        result = false;
        break;
      }
    } else if (!(
          arrValue === othValue ||
            equalFunc(arrValue, othValue, bitmask, customizer, stack)
        )) {
      result = false;
      break;
    }
  }
  stack['delete'](array);
  stack['delete'](other);
  return result;
}

module.exports = equalArrays;

},{"./_SetCache":53,"./_arraySome":62,"./_cacheHas":84}],89:[function(_dereq_,module,exports){
var Symbol = _dereq_('./_Symbol'),
    Uint8Array = _dereq_('./_Uint8Array'),
    eq = _dereq_('./eq'),
    equalArrays = _dereq_('./_equalArrays'),
    mapToArray = _dereq_('./_mapToArray'),
    setToArray = _dereq_('./_setToArray');

/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1,
    COMPARE_UNORDERED_FLAG = 2;

/** `Object#toString` result references. */
var boolTag = '[object Boolean]',
    dateTag = '[object Date]',
    errorTag = '[object Error]',
    mapTag = '[object Map]',
    numberTag = '[object Number]',
    regexpTag = '[object RegExp]',
    setTag = '[object Set]',
    stringTag = '[object String]',
    symbolTag = '[object Symbol]';

var arrayBufferTag = '[object ArrayBuffer]',
    dataViewTag = '[object DataView]';

/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol ? Symbol.prototype : undefined,
    symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;

/**
 * A specialized version of `baseIsEqualDeep` for comparing objects of
 * the same `toStringTag`.
 *
 * **Note:** This function only supports comparing values with tags of
 * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
 *
 * @private
 * @param {Object} object The object to compare.
 * @param {Object} other The other object to compare.
 * @param {string} tag The `toStringTag` of the objects to compare.
 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} stack Tracks traversed `object` and `other` objects.
 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
 */
function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {
  switch (tag) {
    case dataViewTag:
      if ((object.byteLength != other.byteLength) ||
          (object.byteOffset != other.byteOffset)) {
        return false;
      }
      object = object.buffer;
      other = other.buffer;

    case arrayBufferTag:
      if ((object.byteLength != other.byteLength) ||
          !equalFunc(new Uint8Array(object), new Uint8Array(other))) {
        return false;
      }
      return true;

    case boolTag:
    case dateTag:
    case numberTag:
      // Coerce booleans to `1` or `0` and dates to milliseconds.
      // Invalid dates are coerced to `NaN`.
      return eq(+object, +other);

    case errorTag:
      return object.name == other.name && object.message == other.message;

    case regexpTag:
    case stringTag:
      // Coerce regexes to strings and treat strings, primitives and objects,
      // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring
      // for more details.
      return object == (other + '');

    case mapTag:
      var convert = mapToArray;

    case setTag:
      var isPartial = bitmask & COMPARE_PARTIAL_FLAG;
      convert || (convert = setToArray);

      if (object.size != other.size && !isPartial) {
        return false;
      }
      // Assume cyclic values are equal.
      var stacked = stack.get(object);
      if (stacked) {
        return stacked == other;
      }
      bitmask |= COMPARE_UNORDERED_FLAG;

      // Recursively compare objects (susceptible to call stack limits).
      stack.set(object, other);
      var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);
      stack['delete'](object);
      return result;

    case symbolTag:
      if (symbolValueOf) {
        return symbolValueOf.call(object) == symbolValueOf.call(other);
      }
  }
  return false;
}

module.exports = equalByTag;

},{"./_Symbol":55,"./_Uint8Array":56,"./_equalArrays":88,"./_mapToArray":122,"./_setToArray":133,"./eq":142}],90:[function(_dereq_,module,exports){
var getAllKeys = _dereq_('./_getAllKeys');

/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1;

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * A specialized version of `baseIsEqualDeep` for objects with support for
 * partial deep comparisons.
 *
 * @private
 * @param {Object} object The object to compare.
 * @param {Object} other The other object to compare.
 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} stack Tracks traversed `object` and `other` objects.
 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
 */
function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {
  var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
      objProps = getAllKeys(object),
      objLength = objProps.length,
      othProps = getAllKeys(other),
      othLength = othProps.length;

  if (objLength != othLength && !isPartial) {
    return false;
  }
  var index = objLength;
  while (index--) {
    var key = objProps[index];
    if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {
      return false;
    }
  }
  // Assume cyclic values are equal.
  var stacked = stack.get(object);
  if (stacked && stack.get(other)) {
    return stacked == other;
  }
  var result = true;
  stack.set(object, other);
  stack.set(other, object);

  var skipCtor = isPartial;
  while (++index < objLength) {
    key = objProps[index];
    var objValue = object[key],
        othValue = other[key];

    if (customizer) {
      var compared = isPartial
        ? customizer(othValue, objValue, key, other, object, stack)
        : customizer(objValue, othValue, key, object, other, stack);
    }
    // Recursively compare objects (susceptible to call stack limits).
    if (!(compared === undefined
          ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack))
          : compared
        )) {
      result = false;
      break;
    }
    skipCtor || (skipCtor = key == 'constructor');
  }
  if (result && !skipCtor) {
    var objCtor = object.constructor,
        othCtor = other.constructor;

    // Non `Object` object instances with different constructors are not equal.
    if (objCtor != othCtor &&
        ('constructor' in object && 'constructor' in other) &&
        !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
          typeof othCtor == 'function' && othCtor instanceof othCtor)) {
      result = false;
    }
  }
  stack['delete'](object);
  stack['delete'](other);
  return result;
}

module.exports = equalObjects;

},{"./_getAllKeys":92}],91:[function(_dereq_,module,exports){
(function (global){
/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;

module.exports = freeGlobal;

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{}],92:[function(_dereq_,module,exports){
var baseGetAllKeys = _dereq_('./_baseGetAllKeys'),
    getSymbols = _dereq_('./_getSymbols'),
    keys = _dereq_('./keys');

/**
 * Creates an array of own enumerable property names and symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names and symbols.
 */
function getAllKeys(object) {
  return baseGetAllKeys(object, keys, getSymbols);
}

module.exports = getAllKeys;

},{"./_baseGetAllKeys":66,"./_getSymbols":97,"./keys":158}],93:[function(_dereq_,module,exports){
var isKeyable = _dereq_('./_isKeyable');

/**
 * Gets the data for `map`.
 *
 * @private
 * @param {Object} map The map to query.
 * @param {string} key The reference key.
 * @returns {*} Returns the map data.
 */
function getMapData(map, key) {
  var data = map.__data__;
  return isKeyable(key)
    ? data[typeof key == 'string' ? 'string' : 'hash']
    : data.map;
}

module.exports = getMapData;

},{"./_isKeyable":108}],94:[function(_dereq_,module,exports){
var isStrictComparable = _dereq_('./_isStrictComparable'),
    keys = _dereq_('./keys');

/**
 * Gets the property names, values, and compare flags of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the match data of `object`.
 */
function getMatchData(object) {
  var result = keys(object),
      length = result.length;

  while (length--) {
    var key = result[length],
        value = object[key];

    result[length] = [key, value, isStrictComparable(value)];
  }
  return result;
}

module.exports = getMatchData;

},{"./_isStrictComparable":111,"./keys":158}],95:[function(_dereq_,module,exports){
var baseIsNative = _dereq_('./_baseIsNative'),
    getValue = _dereq_('./_getValue');

/**
 * Gets the native function at `key` of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {string} key The key of the method to get.
 * @returns {*} Returns the function if it's native, else `undefined`.
 */
function getNative(object, key) {
  var value = getValue(object, key);
  return baseIsNative(value) ? value : undefined;
}

module.exports = getNative;

},{"./_baseIsNative":73,"./_getValue":99}],96:[function(_dereq_,module,exports){
var Symbol = _dereq_('./_Symbol');

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString = objectProto.toString;

/** Built-in value references. */
var symToStringTag = Symbol ? Symbol.toStringTag : undefined;

/**
 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the raw `toStringTag`.
 */
function getRawTag(value) {
  var isOwn = hasOwnProperty.call(value, symToStringTag),
      tag = value[symToStringTag];

  try {
    value[symToStringTag] = undefined;
    var unmasked = true;
  } catch (e) {}

  var result = nativeObjectToString.call(value);
  if (unmasked) {
    if (isOwn) {
      value[symToStringTag] = tag;
    } else {
      delete value[symToStringTag];
    }
  }
  return result;
}

module.exports = getRawTag;

},{"./_Symbol":55}],97:[function(_dereq_,module,exports){
var arrayFilter = _dereq_('./_arrayFilter'),
    stubArray = _dereq_('./stubArray');

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Built-in value references. */
var propertyIsEnumerable = objectProto.propertyIsEnumerable;

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeGetSymbols = Object.getOwnPropertySymbols;

/**
 * Creates an array of the own enumerable symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of symbols.
 */
var getSymbols = !nativeGetSymbols ? stubArray : function(object) {
  if (object == null) {
    return [];
  }
  object = Object(object);
  return arrayFilter(nativeGetSymbols(object), function(symbol) {
    return propertyIsEnumerable.call(object, symbol);
  });
};

module.exports = getSymbols;

},{"./_arrayFilter":58,"./stubArray":161}],98:[function(_dereq_,module,exports){
var DataView = _dereq_('./_DataView'),
    Map = _dereq_('./_Map'),
    Promise = _dereq_('./_Promise'),
    Set = _dereq_('./_Set'),
    WeakMap = _dereq_('./_WeakMap'),
    baseGetTag = _dereq_('./_baseGetTag'),
    toSource = _dereq_('./_toSource');

/** `Object#toString` result references. */
var mapTag = '[object Map]',
    objectTag = '[object Object]',
    promiseTag = '[object Promise]',
    setTag = '[object Set]',
    weakMapTag = '[object WeakMap]';

var dataViewTag = '[object DataView]';

/** Used to detect maps, sets, and weakmaps. */
var dataViewCtorString = toSource(DataView),
    mapCtorString = toSource(Map),
    promiseCtorString = toSource(Promise),
    setCtorString = toSource(Set),
    weakMapCtorString = toSource(WeakMap);

/**
 * Gets the `toStringTag` of `value`.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
var getTag = baseGetTag;

// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
    (Map && getTag(new Map) != mapTag) ||
    (Promise && getTag(Promise.resolve()) != promiseTag) ||
    (Set && getTag(new Set) != setTag) ||
    (WeakMap && getTag(new WeakMap) != weakMapTag)) {
  getTag = function(value) {
    var result = baseGetTag(value),
        Ctor = result == objectTag ? value.constructor : undefined,
        ctorString = Ctor ? toSource(Ctor) : '';

    if (ctorString) {
      switch (ctorString) {
        case dataViewCtorString: return dataViewTag;
        case mapCtorString: return mapTag;
        case promiseCtorString: return promiseTag;
        case setCtorString: return setTag;
        case weakMapCtorString: return weakMapTag;
      }
    }
    return result;
  };
}

module.exports = getTag;

},{"./_DataView":46,"./_Map":49,"./_Promise":51,"./_Set":52,"./_WeakMap":57,"./_baseGetTag":67,"./_toSource":141}],99:[function(_dereq_,module,exports){
/**
 * Gets the value at `key` of `object`.
 *
 * @private
 * @param {Object} [object] The object to query.
 * @param {string} key The key of the property to get.
 * @returns {*} Returns the property value.
 */
function getValue(object, key) {
  return object == null ? undefined : object[key];
}

module.exports = getValue;

},{}],100:[function(_dereq_,module,exports){
var castPath = _dereq_('./_castPath'),
    isArguments = _dereq_('./isArguments'),
    isArray = _dereq_('./isArray'),
    isIndex = _dereq_('./_isIndex'),
    isLength = _dereq_('./isLength'),
    toKey = _dereq_('./_toKey');

/**
 * Checks if `path` exists on `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {Array|string} path The path to check.
 * @param {Function} hasFunc The function to check properties.
 * @returns {boolean} Returns `true` if `path` exists, else `false`.
 */
function hasPath(object, path, hasFunc) {
  path = castPath(path, object);

  var index = -1,
      length = path.length,
      result = false;

  while (++index < length) {
    var key = toKey(path[index]);
    if (!(result = object != null && hasFunc(object, key))) {
      break;
    }
    object = object[key];
  }
  if (result || ++index != length) {
    return result;
  }
  length = object == null ? 0 : object.length;
  return !!length && isLength(length) && isIndex(key, length) &&
    (isArray(object) || isArguments(object));
}

module.exports = hasPath;

},{"./_castPath":85,"./_isIndex":106,"./_toKey":140,"./isArguments":148,"./isArray":149,"./isLength":153}],101:[function(_dereq_,module,exports){
var nativeCreate = _dereq_('./_nativeCreate');

/**
 * Removes all key-value entries from the hash.
 *
 * @private
 * @name clear
 * @memberOf Hash
 */
function hashClear() {
  this.__data__ = nativeCreate ? nativeCreate(null) : {};
  this.size = 0;
}

module.exports = hashClear;

},{"./_nativeCreate":125}],102:[function(_dereq_,module,exports){
/**
 * Removes `key` and its value from the hash.
 *
 * @private
 * @name delete
 * @memberOf Hash
 * @param {Object} hash The hash to modify.
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function hashDelete(key) {
  var result = this.has(key) && delete this.__data__[key];
  this.size -= result ? 1 : 0;
  return result;
}

module.exports = hashDelete;

},{}],103:[function(_dereq_,module,exports){
var nativeCreate = _dereq_('./_nativeCreate');

/** Used to stand-in for `undefined` hash values. */
var HASH_UNDEFINED = '__lodash_hash_undefined__';

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Gets the hash value for `key`.
 *
 * @private
 * @name get
 * @memberOf Hash
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function hashGet(key) {
  var data = this.__data__;
  if (nativeCreate) {
    var result = data[key];
    return result === HASH_UNDEFINED ? undefined : result;
  }
  return hasOwnProperty.call(data, key) ? data[key] : undefined;
}

module.exports = hashGet;

},{"./_nativeCreate":125}],104:[function(_dereq_,module,exports){
var nativeCreate = _dereq_('./_nativeCreate');

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Checks if a hash value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf Hash
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function hashHas(key) {
  var data = this.__data__;
  return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key);
}

module.exports = hashHas;

},{"./_nativeCreate":125}],105:[function(_dereq_,module,exports){
var nativeCreate = _dereq_('./_nativeCreate');

/** Used to stand-in for `undefined` hash values. */
var HASH_UNDEFINED = '__lodash_hash_undefined__';

/**
 * Sets the hash `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf Hash
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the hash instance.
 */
function hashSet(key, value) {
  var data = this.__data__;
  this.size += this.has(key) ? 0 : 1;
  data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
  return this;
}

module.exports = hashSet;

},{"./_nativeCreate":125}],106:[function(_dereq_,module,exports){
/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER = 9007199254740991;

/** Used to detect unsigned integer values. */
var reIsUint = /^(?:0|[1-9]\d*)$/;

/**
 * Checks if `value` is a valid array-like index.
 *
 * @private
 * @param {*} value The value to check.
 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
 */
function isIndex(value, length) {
  var type = typeof value;
  length = length == null ? MAX_SAFE_INTEGER : length;

  return !!length &&
    (type == 'number' ||
      (type != 'symbol' && reIsUint.test(value))) &&
        (value > -1 && value % 1 == 0 && value < length);
}

module.exports = isIndex;

},{}],107:[function(_dereq_,module,exports){
var isArray = _dereq_('./isArray'),
    isSymbol = _dereq_('./isSymbol');

/** Used to match property names within property paths. */
var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
    reIsPlainProp = /^\w*$/;

/**
 * Checks if `value` is a property name and not a property path.
 *
 * @private
 * @param {*} value The value to check.
 * @param {Object} [object] The object to query keys on.
 * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
 */
function isKey(value, object) {
  if (isArray(value)) {
    return false;
  }
  var type = typeof value;
  if (type == 'number' || type == 'symbol' || type == 'boolean' ||
      value == null || isSymbol(value)) {
    return true;
  }
  return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
    (object != null && value in Object(object));
}

module.exports = isKey;

},{"./isArray":149,"./isSymbol":156}],108:[function(_dereq_,module,exports){
/**
 * Checks if `value` is suitable for use as unique object key.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
 */
function isKeyable(value) {
  var type = typeof value;
  return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
    ? (value !== '__proto__')
    : (value === null);
}

module.exports = isKeyable;

},{}],109:[function(_dereq_,module,exports){
var coreJsData = _dereq_('./_coreJsData');

/** Used to detect methods masquerading as native. */
var maskSrcKey = (function() {
  var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
  return uid ? ('Symbol(src)_1.' + uid) : '';
}());

/**
 * Checks if `func` has its source masked.
 *
 * @private
 * @param {Function} func The function to check.
 * @returns {boolean} Returns `true` if `func` is masked, else `false`.
 */
function isMasked(func) {
  return !!maskSrcKey && (maskSrcKey in func);
}

module.exports = isMasked;

},{"./_coreJsData":86}],110:[function(_dereq_,module,exports){
/** Used for built-in method references. */
var objectProto = Object.prototype;

/**
 * Checks if `value` is likely a prototype object.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
 */
function isPrototype(value) {
  var Ctor = value && value.constructor,
      proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;

  return value === proto;
}

module.exports = isPrototype;

},{}],111:[function(_dereq_,module,exports){
var isObject = _dereq_('./isObject');

/**
 * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` if suitable for strict
 *  equality comparisons, else `false`.
 */
function isStrictComparable(value) {
  return value === value && !isObject(value);
}

module.exports = isStrictComparable;

},{"./isObject":154}],112:[function(_dereq_,module,exports){
/**
 * Removes all key-value entries from the list cache.
 *
 * @private
 * @name clear
 * @memberOf ListCache
 */
function listCacheClear() {
  this.__data__ = [];
  this.size = 0;
}

module.exports = listCacheClear;

},{}],113:[function(_dereq_,module,exports){
var assocIndexOf = _dereq_('./_assocIndexOf');

/** Used for built-in method references. */
var arrayProto = Array.prototype;

/** Built-in value references. */
var splice = arrayProto.splice;

/**
 * Removes `key` and its value from the list cache.
 *
 * @private
 * @name delete
 * @memberOf ListCache
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function listCacheDelete(key) {
  var data = this.__data__,
      index = assocIndexOf(data, key);

  if (index < 0) {
    return false;
  }
  var lastIndex = data.length - 1;
  if (index == lastIndex) {
    data.pop();
  } else {
    splice.call(data, index, 1);
  }
  --this.size;
  return true;
}

module.exports = listCacheDelete;

},{"./_assocIndexOf":63}],114:[function(_dereq_,module,exports){
var assocIndexOf = _dereq_('./_assocIndexOf');

/**
 * Gets the list cache value for `key`.
 *
 * @private
 * @name get
 * @memberOf ListCache
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function listCacheGet(key) {
  var data = this.__data__,
      index = assocIndexOf(data, key);

  return index < 0 ? undefined : data[index][1];
}

module.exports = listCacheGet;

},{"./_assocIndexOf":63}],115:[function(_dereq_,module,exports){
var assocIndexOf = _dereq_('./_assocIndexOf');

/**
 * Checks if a list cache value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf ListCache
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function listCacheHas(key) {
  return assocIndexOf(this.__data__, key) > -1;
}

module.exports = listCacheHas;

},{"./_assocIndexOf":63}],116:[function(_dereq_,module,exports){
var assocIndexOf = _dereq_('./_assocIndexOf');

/**
 * Sets the list cache `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf ListCache
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the list cache instance.
 */
function listCacheSet(key, value) {
  var data = this.__data__,
      index = assocIndexOf(data, key);

  if (index < 0) {
    ++this.size;
    data.push([key, value]);
  } else {
    data[index][1] = value;
  }
  return this;
}

module.exports = listCacheSet;

},{"./_assocIndexOf":63}],117:[function(_dereq_,module,exports){
var Hash = _dereq_('./_Hash'),
    ListCache = _dereq_('./_ListCache'),
    Map = _dereq_('./_Map');

/**
 * Removes all key-value entries from the map.
 *
 * @private
 * @name clear
 * @memberOf MapCache
 */
function mapCacheClear() {
  this.size = 0;
  this.__data__ = {
    'hash': new Hash,
    'map': new (Map || ListCache),
    'string': new Hash
  };
}

module.exports = mapCacheClear;

},{"./_Hash":47,"./_ListCache":48,"./_Map":49}],118:[function(_dereq_,module,exports){
var getMapData = _dereq_('./_getMapData');

/**
 * Removes `key` and its value from the map.
 *
 * @private
 * @name delete
 * @memberOf MapCache
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function mapCacheDelete(key) {
  var result = getMapData(this, key)['delete'](key);
  this.size -= result ? 1 : 0;
  return result;
}

module.exports = mapCacheDelete;

},{"./_getMapData":93}],119:[function(_dereq_,module,exports){
var getMapData = _dereq_('./_getMapData');

/**
 * Gets the map value for `key`.
 *
 * @private
 * @name get
 * @memberOf MapCache
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function mapCacheGet(key) {
  return getMapData(this, key).get(key);
}

module.exports = mapCacheGet;

},{"./_getMapData":93}],120:[function(_dereq_,module,exports){
var getMapData = _dereq_('./_getMapData');

/**
 * Checks if a map value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf MapCache
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function mapCacheHas(key) {
  return getMapData(this, key).has(key);
}

module.exports = mapCacheHas;

},{"./_getMapData":93}],121:[function(_dereq_,module,exports){
var getMapData = _dereq_('./_getMapData');

/**
 * Sets the map `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf MapCache
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the map cache instance.
 */
function mapCacheSet(key, value) {
  var data = getMapData(this, key),
      size = data.size;

  data.set(key, value);
  this.size += data.size == size ? 0 : 1;
  return this;
}

module.exports = mapCacheSet;

},{"./_getMapData":93}],122:[function(_dereq_,module,exports){
/**
 * Converts `map` to its key-value pairs.
 *
 * @private
 * @param {Object} map The map to convert.
 * @returns {Array} Returns the key-value pairs.
 */
function mapToArray(map) {
  var index = -1,
      result = Array(map.size);

  map.forEach(function(value, key) {
    result[++index] = [key, value];
  });
  return result;
}

module.exports = mapToArray;

},{}],123:[function(_dereq_,module,exports){
/**
 * A specialized version of `matchesProperty` for source values suitable
 * for strict equality comparisons, i.e. `===`.
 *
 * @private
 * @param {string} key The key of the property to get.
 * @param {*} srcValue The value to match.
 * @returns {Function} Returns the new spec function.
 */
function matchesStrictComparable(key, srcValue) {
  return function(object) {
    if (object == null) {
      return false;
    }
    return object[key] === srcValue &&
      (srcValue !== undefined || (key in Object(object)));
  };
}

module.exports = matchesStrictComparable;

},{}],124:[function(_dereq_,module,exports){
var memoize = _dereq_('./memoize');

/** Used as the maximum memoize cache size. */
var MAX_MEMOIZE_SIZE = 500;

/**
 * A specialized version of `_.memoize` which clears the memoized function's
 * cache when it exceeds `MAX_MEMOIZE_SIZE`.
 *
 * @private
 * @param {Function} func The function to have its output memoized.
 * @returns {Function} Returns the new memoized function.
 */
function memoizeCapped(func) {
  var result = memoize(func, function(key) {
    if (cache.size === MAX_MEMOIZE_SIZE) {
      cache.clear();
    }
    return key;
  });

  var cache = result.cache;
  return result;
}

module.exports = memoizeCapped;

},{"./memoize":159}],125:[function(_dereq_,module,exports){
var getNative = _dereq_('./_getNative');

/* Built-in method references that are verified to be native. */
var nativeCreate = getNative(Object, 'create');

module.exports = nativeCreate;

},{"./_getNative":95}],126:[function(_dereq_,module,exports){
var overArg = _dereq_('./_overArg');

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeKeys = overArg(Object.keys, Object);

module.exports = nativeKeys;

},{"./_overArg":129}],127:[function(_dereq_,module,exports){
var freeGlobal = _dereq_('./_freeGlobal');

/** Detect free variable `exports`. */
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;

/** Detect free variable `module`. */
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;

/** Detect the popular CommonJS extension `module.exports`. */
var moduleExports = freeModule && freeModule.exports === freeExports;

/** Detect free variable `process` from Node.js. */
var freeProcess = moduleExports && freeGlobal.process;

/** Used to access faster Node.js helpers. */
var nodeUtil = (function() {
  try {
    // Use `util.types` for Node.js 10+.
    var types = freeModule && freeModule.require && freeModule.require('util').types;

    if (types) {
      return types;
    }

    // Legacy `process.binding('util')` for Node.js < 10.
    return freeProcess && freeProcess.binding && freeProcess.binding('util');
  } catch (e) {}
}());

module.exports = nodeUtil;

},{"./_freeGlobal":91}],128:[function(_dereq_,module,exports){
/** Used for built-in method references. */
var objectProto = Object.prototype;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString = objectProto.toString;

/**
 * Converts `value` to a string using `Object.prototype.toString`.
 *
 * @private
 * @param {*} value The value to convert.
 * @returns {string} Returns the converted string.
 */
function objectToString(value) {
  return nativeObjectToString.call(value);
}

module.exports = objectToString;

},{}],129:[function(_dereq_,module,exports){
/**
 * Creates a unary function that invokes `func` with its argument transformed.
 *
 * @private
 * @param {Function} func The function to wrap.
 * @param {Function} transform The argument transform.
 * @returns {Function} Returns the new function.
 */
function overArg(func, transform) {
  return function(arg) {
    return func(transform(arg));
  };
}

module.exports = overArg;

},{}],130:[function(_dereq_,module,exports){
var freeGlobal = _dereq_('./_freeGlobal');

/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;

/** Used as a reference to the global object. */
var root = freeGlobal || freeSelf || Function('return this')();

module.exports = root;

},{"./_freeGlobal":91}],131:[function(_dereq_,module,exports){
/** Used to stand-in for `undefined` hash values. */
var HASH_UNDEFINED = '__lodash_hash_undefined__';

/**
 * Adds `value` to the array cache.
 *
 * @private
 * @name add
 * @memberOf SetCache
 * @alias push
 * @param {*} value The value to cache.
 * @returns {Object} Returns the cache instance.
 */
function setCacheAdd(value) {
  this.__data__.set(value, HASH_UNDEFINED);
  return this;
}

module.exports = setCacheAdd;

},{}],132:[function(_dereq_,module,exports){
/**
 * Checks if `value` is in the array cache.
 *
 * @private
 * @name has
 * @memberOf SetCache
 * @param {*} value The value to search for.
 * @returns {number} Returns `true` if `value` is found, else `false`.
 */
function setCacheHas(value) {
  return this.__data__.has(value);
}

module.exports = setCacheHas;

},{}],133:[function(_dereq_,module,exports){
/**
 * Converts `set` to an array of its values.
 *
 * @private
 * @param {Object} set The set to convert.
 * @returns {Array} Returns the values.
 */
function setToArray(set) {
  var index = -1,
      result = Array(set.size);

  set.forEach(function(value) {
    result[++index] = value;
  });
  return result;
}

module.exports = setToArray;

},{}],134:[function(_dereq_,module,exports){
var ListCache = _dereq_('./_ListCache');

/**
 * Removes all key-value entries from the stack.
 *
 * @private
 * @name clear
 * @memberOf Stack
 */
function stackClear() {
  this.__data__ = new ListCache;
  this.size = 0;
}

module.exports = stackClear;

},{"./_ListCache":48}],135:[function(_dereq_,module,exports){
/**
 * Removes `key` and its value from the stack.
 *
 * @private
 * @name delete
 * @memberOf Stack
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function stackDelete(key) {
  var data = this.__data__,
      result = data['delete'](key);

  this.size = data.size;
  return result;
}

module.exports = stackDelete;

},{}],136:[function(_dereq_,module,exports){
/**
 * Gets the stack value for `key`.
 *
 * @private
 * @name get
 * @memberOf Stack
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function stackGet(key) {
  return this.__data__.get(key);
}

module.exports = stackGet;

},{}],137:[function(_dereq_,module,exports){
/**
 * Checks if a stack value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf Stack
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function stackHas(key) {
  return this.__data__.has(key);
}

module.exports = stackHas;

},{}],138:[function(_dereq_,module,exports){
var ListCache = _dereq_('./_ListCache'),
    Map = _dereq_('./_Map'),
    MapCache = _dereq_('./_MapCache');

/** Used as the size to enable large array optimizations. */
var LARGE_ARRAY_SIZE = 200;

/**
 * Sets the stack `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf Stack
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the stack cache instance.
 */
function stackSet(key, value) {
  var data = this.__data__;
  if (data instanceof ListCache) {
    var pairs = data.__data__;
    if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {
      pairs.push([key, value]);
      this.size = ++data.size;
      return this;
    }
    data = this.__data__ = new MapCache(pairs);
  }
  data.set(key, value);
  this.size = data.size;
  return this;
}

module.exports = stackSet;

},{"./_ListCache":48,"./_Map":49,"./_MapCache":50}],139:[function(_dereq_,module,exports){
var memoizeCapped = _dereq_('./_memoizeCapped');

/** Used to match property names within property paths. */
var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;

/** Used to match backslashes in property paths. */
var reEscapeChar = /\\(\\)?/g;

/**
 * Converts `string` to a property path array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the property path array.
 */
var stringToPath = memoizeCapped(function(string) {
  var result = [];
  if (string.charCodeAt(0) === 46 /* . */) {
    result.push('');
  }
  string.replace(rePropName, function(match, number, quote, subString) {
    result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));
  });
  return result;
});

module.exports = stringToPath;

},{"./_memoizeCapped":124}],140:[function(_dereq_,module,exports){
var isSymbol = _dereq_('./isSymbol');

/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0;

/**
 * Converts `value` to a string key if it's not a string or symbol.
 *
 * @private
 * @param {*} value The value to inspect.
 * @returns {string|symbol} Returns the key.
 */
function toKey(value) {
  if (typeof value == 'string' || isSymbol(value)) {
    return value;
  }
  var result = (value + '');
  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
}

module.exports = toKey;

},{"./isSymbol":156}],141:[function(_dereq_,module,exports){
/** Used for built-in method references. */
var funcProto = Function.prototype;

/** Used to resolve the decompiled source of functions. */
var funcToString = funcProto.toString;

/**
 * Converts `func` to its source code.
 *
 * @private
 * @param {Function} func The function to convert.
 * @returns {string} Returns the source code.
 */
function toSource(func) {
  if (func != null) {
    try {
      return funcToString.call(func);
    } catch (e) {}
    try {
      return (func + '');
    } catch (e) {}
  }
  return '';
}

module.exports = toSource;

},{}],142:[function(_dereq_,module,exports){
/**
 * Performs a
 * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
 * comparison between two values to determine if they are equivalent.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to compare.
 * @param {*} other The other value to compare.
 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
 * @example
 *
 * var object = { 'a': 1 };
 * var other = { 'a': 1 };
 *
 * _.eq(object, object);
 * // => true
 *
 * _.eq(object, other);
 * // => false
 *
 * _.eq('a', 'a');
 * // => true
 *
 * _.eq('a', Object('a'));
 * // => false
 *
 * _.eq(NaN, NaN);
 * // => true
 */
function eq(value, other) {
  return value === other || (value !== value && other !== other);
}

module.exports = eq;

},{}],143:[function(_dereq_,module,exports){
var createFind = _dereq_('./_createFind'),
    findIndex = _dereq_('./findIndex');

/**
 * Iterates over elements of `collection`, returning the first element
 * `predicate` returns truthy for. The predicate is invoked with three
 * arguments: (value, index|key, collection).
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Collection
 * @param {Array|Object} collection The collection to inspect.
 * @param {Function} [predicate=_.identity] The function invoked per iteration.
 * @param {number} [fromIndex=0] The index to search from.
 * @returns {*} Returns the matched element, else `undefined`.
 * @example
 *
 * var users = [
 *   { 'user': 'barney',  'age': 36, 'active': true },
 *   { 'user': 'fred',    'age': 40, 'active': false },
 *   { 'user': 'pebbles', 'age': 1,  'active': true }
 * ];
 *
 * _.find(users, function(o) { return o.age < 40; });
 * // => object for 'barney'
 *
 * // The `_.matches` iteratee shorthand.
 * _.find(users, { 'age': 1, 'active': true });
 * // => object for 'pebbles'
 *
 * // The `_.matchesProperty` iteratee shorthand.
 * _.find(users, ['active', false]);
 * // => object for 'fred'
 *
 * // The `_.property` iteratee shorthand.
 * _.find(users, 'active');
 * // => object for 'barney'
 */
var find = createFind(findIndex);

module.exports = find;

},{"./_createFind":87,"./findIndex":144}],144:[function(_dereq_,module,exports){
var baseFindIndex = _dereq_('./_baseFindIndex'),
    baseIteratee = _dereq_('./_baseIteratee'),
    toInteger = _dereq_('./toInteger');

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;

/**
 * This method is like `_.find` except that it returns the index of the first
 * element `predicate` returns truthy for instead of the element itself.
 *
 * @static
 * @memberOf _
 * @since 1.1.0
 * @category Array
 * @param {Array} array The array to inspect.
 * @param {Function} [predicate=_.identity] The function invoked per iteration.
 * @param {number} [fromIndex=0] The index to search from.
 * @returns {number} Returns the index of the found element, else `-1`.
 * @example
 *
 * var users = [
 *   { 'user': 'barney',  'active': false },
 *   { 'user': 'fred',    'active': false },
 *   { 'user': 'pebbles', 'active': true }
 * ];
 *
 * _.findIndex(users, function(o) { return o.user == 'barney'; });
 * // => 0
 *
 * // The `_.matches` iteratee shorthand.
 * _.findIndex(users, { 'user': 'fred', 'active': false });
 * // => 1
 *
 * // The `_.matchesProperty` iteratee shorthand.
 * _.findIndex(users, ['active', false]);
 * // => 0
 *
 * // The `_.property` iteratee shorthand.
 * _.findIndex(users, 'active');
 * // => 2
 */
function findIndex(array, predicate, fromIndex) {
  var length = array == null ? 0 : array.length;
  if (!length) {
    return -1;
  }
  var index = fromIndex == null ? 0 : toInteger(fromIndex);
  if (index < 0) {
    index = nativeMax(length + index, 0);
  }
  return baseFindIndex(array, baseIteratee(predicate, 3), index);
}

module.exports = findIndex;

},{"./_baseFindIndex":64,"./_baseIteratee":75,"./toInteger":164}],145:[function(_dereq_,module,exports){
var baseGet = _dereq_('./_baseGet');

/**
 * Gets the value at `path` of `object`. If the resolved value is
 * `undefined`, the `defaultValue` is returned in its place.
 *
 * @static
 * @memberOf _
 * @since 3.7.0
 * @category Object
 * @param {Object} object The object to query.
 * @param {Array|string} path The path of the property to get.
 * @param {*} [defaultValue] The value returned for `undefined` resolved values.
 * @returns {*} Returns the resolved value.
 * @example
 *
 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
 *
 * _.get(object, 'a[0].b.c');
 * // => 3
 *
 * _.get(object, ['a', '0', 'b', 'c']);
 * // => 3
 *
 * _.get(object, 'a.b.c', 'default');
 * // => 'default'
 */
function get(object, path, defaultValue) {
  var result = object == null ? undefined : baseGet(object, path);
  return result === undefined ? defaultValue : result;
}

module.exports = get;

},{"./_baseGet":65}],146:[function(_dereq_,module,exports){
var baseHasIn = _dereq_('./_baseHasIn'),
    hasPath = _dereq_('./_hasPath');

/**
 * Checks if `path` is a direct or inherited property of `object`.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Object
 * @param {Object} object The object to query.
 * @param {Array|string} path The path to check.
 * @returns {boolean} Returns `true` if `path` exists, else `false`.
 * @example
 *
 * var object = _.create({ 'a': _.create({ 'b': 2 }) });
 *
 * _.hasIn(object, 'a');
 * // => true
 *
 * _.hasIn(object, 'a.b');
 * // => true
 *
 * _.hasIn(object, ['a', 'b']);
 * // => true
 *
 * _.hasIn(object, 'b');
 * // => false
 */
function hasIn(object, path) {
  return object != null && hasPath(object, path, baseHasIn);
}

module.exports = hasIn;

},{"./_baseHasIn":68,"./_hasPath":100}],147:[function(_dereq_,module,exports){
/**
 * This method returns the first argument it receives.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Util
 * @param {*} value Any value.
 * @returns {*} Returns `value`.
 * @example
 *
 * var object = { 'a': 1 };
 *
 * console.log(_.identity(object) === object);
 * // => true
 */
function identity(value) {
  return value;
}

module.exports = identity;

},{}],148:[function(_dereq_,module,exports){
var baseIsArguments = _dereq_('./_baseIsArguments'),
    isObjectLike = _dereq_('./isObjectLike');

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/** Built-in value references. */
var propertyIsEnumerable = objectProto.propertyIsEnumerable;

/**
 * Checks if `value` is likely an `arguments` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
 *  else `false`.
 * @example
 *
 * _.isArguments(function() { return arguments; }());
 * // => true
 *
 * _.isArguments([1, 2, 3]);
 * // => false
 */
var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {
  return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&
    !propertyIsEnumerable.call(value, 'callee');
};

module.exports = isArguments;

},{"./_baseIsArguments":69,"./isObjectLike":155}],149:[function(_dereq_,module,exports){
/**
 * Checks if `value` is classified as an `Array` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an array, else `false`.
 * @example
 *
 * _.isArray([1, 2, 3]);
 * // => true
 *
 * _.isArray(document.body.children);
 * // => false
 *
 * _.isArray('abc');
 * // => false
 *
 * _.isArray(_.noop);
 * // => false
 */
var isArray = Array.isArray;

module.exports = isArray;

},{}],150:[function(_dereq_,module,exports){
var isFunction = _dereq_('./isFunction'),
    isLength = _dereq_('./isLength');

/**
 * Checks if `value` is array-like. A value is considered array-like if it's
 * not a function and has a `value.length` that's an integer greater than or
 * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
 * @example
 *
 * _.isArrayLike([1, 2, 3]);
 * // => true
 *
 * _.isArrayLike(document.body.children);
 * // => true
 *
 * _.isArrayLike('abc');
 * // => true
 *
 * _.isArrayLike(_.noop);
 * // => false
 */
function isArrayLike(value) {
  return value != null && isLength(value.length) && !isFunction(value);
}

module.exports = isArrayLike;

},{"./isFunction":152,"./isLength":153}],151:[function(_dereq_,module,exports){
var root = _dereq_('./_root'),
    stubFalse = _dereq_('./stubFalse');

/** Detect free variable `exports`. */
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;

/** Detect free variable `module`. */
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;

/** Detect the popular CommonJS extension `module.exports`. */
var moduleExports = freeModule && freeModule.exports === freeExports;

/** Built-in value references. */
var Buffer = moduleExports ? root.Buffer : undefined;

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;

/**
 * Checks if `value` is a buffer.
 *
 * @static
 * @memberOf _
 * @since 4.3.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
 * @example
 *
 * _.isBuffer(new Buffer(2));
 * // => true
 *
 * _.isBuffer(new Uint8Array(2));
 * // => false
 */
var isBuffer = nativeIsBuffer || stubFalse;

module.exports = isBuffer;

},{"./_root":130,"./stubFalse":162}],152:[function(_dereq_,module,exports){
var baseGetTag = _dereq_('./_baseGetTag'),
    isObject = _dereq_('./isObject');

/** `Object#toString` result references. */
var asyncTag = '[object AsyncFunction]',
    funcTag = '[object Function]',
    genTag = '[object GeneratorFunction]',
    proxyTag = '[object Proxy]';

/**
 * Checks if `value` is classified as a `Function` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a function, else `false`.
 * @example
 *
 * _.isFunction(_);
 * // => true
 *
 * _.isFunction(/abc/);
 * // => false
 */
function isFunction(value) {
  if (!isObject(value)) {
    return false;
  }
  // The use of `Object#toString` avoids issues with the `typeof` operator
  // in Safari 9 which returns 'object' for typed arrays and other constructors.
  var tag = baseGetTag(value);
  return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
}

module.exports = isFunction;

},{"./_baseGetTag":67,"./isObject":154}],153:[function(_dereq_,module,exports){
/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER = 9007199254740991;

/**
 * Checks if `value` is a valid array-like length.
 *
 * **Note:** This method is loosely based on
 * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
 * @example
 *
 * _.isLength(3);
 * // => true
 *
 * _.isLength(Number.MIN_VALUE);
 * // => false
 *
 * _.isLength(Infinity);
 * // => false
 *
 * _.isLength('3');
 * // => false
 */
function isLength(value) {
  return typeof value == 'number' &&
    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
}

module.exports = isLength;

},{}],154:[function(_dereq_,module,exports){
/**
 * Checks if `value` is the
 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
 * @example
 *
 * _.isObject({});
 * // => true
 *
 * _.isObject([1, 2, 3]);
 * // => true
 *
 * _.isObject(_.noop);
 * // => true
 *
 * _.isObject(null);
 * // => false
 */
function isObject(value) {
  var type = typeof value;
  return value != null && (type == 'object' || type == 'function');
}

module.exports = isObject;

},{}],155:[function(_dereq_,module,exports){
/**
 * Checks if `value` is object-like. A value is object-like if it's not `null`
 * and has a `typeof` result of "object".
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
 * @example
 *
 * _.isObjectLike({});
 * // => true
 *
 * _.isObjectLike([1, 2, 3]);
 * // => true
 *
 * _.isObjectLike(_.noop);
 * // => false
 *
 * _.isObjectLike(null);
 * // => false
 */
function isObjectLike(value) {
  return value != null && typeof value == 'object';
}

module.exports = isObjectLike;

},{}],156:[function(_dereq_,module,exports){
var baseGetTag = _dereq_('./_baseGetTag'),
    isObjectLike = _dereq_('./isObjectLike');

/** `Object#toString` result references. */
var symbolTag = '[object Symbol]';

/**
 * Checks if `value` is classified as a `Symbol` primitive or object.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
 * @example
 *
 * _.isSymbol(Symbol.iterator);
 * // => true
 *
 * _.isSymbol('abc');
 * // => false
 */
function isSymbol(value) {
  return typeof value == 'symbol' ||
    (isObjectLike(value) && baseGetTag(value) == symbolTag);
}

module.exports = isSymbol;

},{"./_baseGetTag":67,"./isObjectLike":155}],157:[function(_dereq_,module,exports){
var baseIsTypedArray = _dereq_('./_baseIsTypedArray'),
    baseUnary = _dereq_('./_baseUnary'),
    nodeUtil = _dereq_('./_nodeUtil');

/* Node.js helper references. */
var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;

/**
 * Checks if `value` is classified as a typed array.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
 * @example
 *
 * _.isTypedArray(new Uint8Array);
 * // => true
 *
 * _.isTypedArray([]);
 * // => false
 */
var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;

module.exports = isTypedArray;

},{"./_baseIsTypedArray":74,"./_baseUnary":83,"./_nodeUtil":127}],158:[function(_dereq_,module,exports){
var arrayLikeKeys = _dereq_('./_arrayLikeKeys'),
    baseKeys = _dereq_('./_baseKeys'),
    isArrayLike = _dereq_('./isArrayLike');

/**
 * Creates an array of the own enumerable property names of `object`.
 *
 * **Note:** Non-object values are coerced to objects. See the
 * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
 * for more details.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Object
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.keys(new Foo);
 * // => ['a', 'b'] (iteration order is not guaranteed)
 *
 * _.keys('hi');
 * // => ['0', '1']
 */
function keys(object) {
  return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
}

module.exports = keys;

},{"./_arrayLikeKeys":59,"./_baseKeys":76,"./isArrayLike":150}],159:[function(_dereq_,module,exports){
var MapCache = _dereq_('./_MapCache');

/** Error message constants. */
var FUNC_ERROR_TEXT = 'Expected a function';

/**
 * Creates a function that memoizes the result of `func`. If `resolver` is
 * provided, it determines the cache key for storing the result based on the
 * arguments provided to the memoized function. By default, the first argument
 * provided to the memoized function is used as the map cache key. The `func`
 * is invoked with the `this` binding of the memoized function.
 *
 * **Note:** The cache is exposed as the `cache` property on the memoized
 * function. Its creation may be customized by replacing the `_.memoize.Cache`
 * constructor with one whose instances implement the
 * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
 * method interface of `clear`, `delete`, `get`, `has`, and `set`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Function
 * @param {Function} func The function to have its output memoized.
 * @param {Function} [resolver] The function to resolve the cache key.
 * @returns {Function} Returns the new memoized function.
 * @example
 *
 * var object = { 'a': 1, 'b': 2 };
 * var other = { 'c': 3, 'd': 4 };
 *
 * var values = _.memoize(_.values);
 * values(object);
 * // => [1, 2]
 *
 * values(other);
 * // => [3, 4]
 *
 * object.a = 2;
 * values(object);
 * // => [1, 2]
 *
 * // Modify the result cache.
 * values.cache.set(object, ['a', 'b']);
 * values(object);
 * // => ['a', 'b']
 *
 * // Replace `_.memoize.Cache`.
 * _.memoize.Cache = WeakMap;
 */
function memoize(func, resolver) {
  if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  var memoized = function() {
    var args = arguments,
        key = resolver ? resolver.apply(this, args) : args[0],
        cache = memoized.cache;

    if (cache.has(key)) {
      return cache.get(key);
    }
    var result = func.apply(this, args);
    memoized.cache = cache.set(key, result) || cache;
    return result;
  };
  memoized.cache = new (memoize.Cache || MapCache);
  return memoized;
}

// Expose `MapCache`.
memoize.Cache = MapCache;

module.exports = memoize;

},{"./_MapCache":50}],160:[function(_dereq_,module,exports){
var baseProperty = _dereq_('./_baseProperty'),
    basePropertyDeep = _dereq_('./_basePropertyDeep'),
    isKey = _dereq_('./_isKey'),
    toKey = _dereq_('./_toKey');

/**
 * Creates a function that returns the value at `path` of a given object.
 *
 * @static
 * @memberOf _
 * @since 2.4.0
 * @category Util
 * @param {Array|string} path The path of the property to get.
 * @returns {Function} Returns the new accessor function.
 * @example
 *
 * var objects = [
 *   { 'a': { 'b': 2 } },
 *   { 'a': { 'b': 1 } }
 * ];
 *
 * _.map(objects, _.property('a.b'));
 * // => [2, 1]
 *
 * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b');
 * // => [1, 2]
 */
function property(path) {
  return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path);
}

module.exports = property;

},{"./_baseProperty":79,"./_basePropertyDeep":80,"./_isKey":107,"./_toKey":140}],161:[function(_dereq_,module,exports){
/**
 * This method returns a new empty array.
 *
 * @static
 * @memberOf _
 * @since 4.13.0
 * @category Util
 * @returns {Array} Returns the new empty array.
 * @example
 *
 * var arrays = _.times(2, _.stubArray);
 *
 * console.log(arrays);
 * // => [[], []]
 *
 * console.log(arrays[0] === arrays[1]);
 * // => false
 */
function stubArray() {
  return [];
}

module.exports = stubArray;

},{}],162:[function(_dereq_,module,exports){
/**
 * This method returns `false`.
 *
 * @static
 * @memberOf _
 * @since 4.13.0
 * @category Util
 * @returns {boolean} Returns `false`.
 * @example
 *
 * _.times(2, _.stubFalse);
 * // => [false, false]
 */
function stubFalse() {
  return false;
}

module.exports = stubFalse;

},{}],163:[function(_dereq_,module,exports){
var toNumber = _dereq_('./toNumber');

/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0,
    MAX_INTEGER = 1.7976931348623157e+308;

/**
 * Converts `value` to a finite number.
 *
 * @static
 * @memberOf _
 * @since 4.12.0
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {number} Returns the converted number.
 * @example
 *
 * _.toFinite(3.2);
 * // => 3.2
 *
 * _.toFinite(Number.MIN_VALUE);
 * // => 5e-324
 *
 * _.toFinite(Infinity);
 * // => 1.7976931348623157e+308
 *
 * _.toFinite('3.2');
 * // => 3.2
 */
function toFinite(value) {
  if (!value) {
    return value === 0 ? value : 0;
  }
  value = toNumber(value);
  if (value === INFINITY || value === -INFINITY) {
    var sign = (value < 0 ? -1 : 1);
    return sign * MAX_INTEGER;
  }
  return value === value ? value : 0;
}

module.exports = toFinite;

},{"./toNumber":165}],164:[function(_dereq_,module,exports){
var toFinite = _dereq_('./toFinite');

/**
 * Converts `value` to an integer.
 *
 * **Note:** This method is loosely based on
 * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {number} Returns the converted integer.
 * @example
 *
 * _.toInteger(3.2);
 * // => 3
 *
 * _.toInteger(Number.MIN_VALUE);
 * // => 0
 *
 * _.toInteger(Infinity);
 * // => 1.7976931348623157e+308
 *
 * _.toInteger('3.2');
 * // => 3
 */
function toInteger(value) {
  var result = toFinite(value),
      remainder = result % 1;

  return result === result ? (remainder ? result - remainder : result) : 0;
}

module.exports = toInteger;

},{"./toFinite":163}],165:[function(_dereq_,module,exports){
var isObject = _dereq_('./isObject'),
    isSymbol = _dereq_('./isSymbol');

/** Used as references for various `Number` constants. */
var NAN = 0 / 0;

/** Used to match leading and trailing whitespace. */
var reTrim = /^\s+|\s+$/g;

/** Used to detect bad signed hexadecimal string values. */
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;

/** Used to detect binary string values. */
var reIsBinary = /^0b[01]+$/i;

/** Used to detect octal string values. */
var reIsOctal = /^0o[0-7]+$/i;

/** Built-in method references without a dependency on `root`. */
var freeParseInt = parseInt;

/**
 * Converts `value` to a number.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to process.
 * @returns {number} Returns the number.
 * @example
 *
 * _.toNumber(3.2);
 * // => 3.2
 *
 * _.toNumber(Number.MIN_VALUE);
 * // => 5e-324
 *
 * _.toNumber(Infinity);
 * // => Infinity
 *
 * _.toNumber('3.2');
 * // => 3.2
 */
function toNumber(value) {
  if (typeof value == 'number') {
    return value;
  }
  if (isSymbol(value)) {
    return NAN;
  }
  if (isObject(value)) {
    var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
    value = isObject(other) ? (other + '') : other;
  }
  if (typeof value != 'string') {
    return value === 0 ? value : +value;
  }
  value = value.replace(reTrim, '');
  var isBinary = reIsBinary.test(value);
  return (isBinary || reIsOctal.test(value))
    ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
    : (reIsBadHex.test(value) ? NAN : +value);
}

module.exports = toNumber;

},{"./isObject":154,"./isSymbol":156}],166:[function(_dereq_,module,exports){
var baseToString = _dereq_('./_baseToString');

/**
 * Converts `value` to a string. An empty string is returned for `null`
 * and `undefined` values. The sign of `-0` is preserved.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {string} Returns the converted string.
 * @example
 *
 * _.toString(null);
 * // => ''
 *
 * _.toString(-0);
 * // => '-0'
 *
 * _.toString([1, 2, 3]);
 * // => '1,2,3'
 */
function toString(value) {
  return value == null ? '' : baseToString(value);
}

module.exports = toString;

},{"./_baseToString":82}],167:[function(_dereq_,module,exports){
(function(window, vjs){
'use strict';
_dereq_('@hola.org/videojs-utils');
var Clipboard = _dereq_('clipboard');
var find = _dereq_('lodash/find');
var vtt = _dereq_('videojs-vtt.js');
var settings_icon_svg = '<svg viewBox="0 0 36 36">'
    +'<path d="m 23.94,18.78 c .03,-0.25 .05,-0.51 .05,-0.78 0,'
    +'-0.27 -0.02,-0.52 -0.05,-0.78 l 1.68,-1.32 c .15,-0.12 .19,-0.33 .09,'
    +'-0.51 l -1.6,-2.76 c -0.09,-0.17 -0.31,-0.24 -0.48,-0.17 l -1.99,.8 c '
    +'-0.41,-0.32 -0.86,-0.58 -1.35,-0.78 l -0.30,-2.12 c -0.02,-0.19 -0.19,'
    +'-0.33 -0.39,-0.33 l -3.2,0 c -0.2,0 -0.36,.14 -0.39,.33 l -0.30,2.12 c '
    +'-0.48,.2 -0.93,.47 -1.35,.78 l -1.99,-0.8 c -0.18,-0.07 -0.39,0 -0.48,'
    +'.17 l -1.6,2.76 c -0.10,.17 -0.05,.39 .09,.51 l 1.68,1.32 c -0.03,.25 '
    +'-0.05,.52 -0.05,.78 0,.26 .02,.52 .05,.78 l -1.68,1.32 c -0.15,.12 '
    +'-0.19,.33 -0.09,.51 l 1.6,2.76 c .09,.17 .31,.24 .48,.17 l 1.99,-0.8 c '
    +'.41,.32 .86,.58 1.35,.78 l .30,2.12 c .02,.19 .19,.33 .39,.33 l 3.2,0 c '
    +'.2,0 .36,-0.14 .39,-0.33 l .30,-2.12 c .48,-0.2 .93,-0.47 1.35,-0.78 l '
    +'1.99,.8 c .18,.07 .39,0 .48,-0.17 l 1.6,-2.76 c .09,-0.17 .05,-0.39 '
    +'-0.09,-0.51 l -1.68,-1.32 0,0 z m -5.94,2.01 c -1.54,0 -2.8,-1.25 -2.8,'
    +'-2.8 0,-1.54 1.25,-2.8 2.8,-2.8 1.54,0 2.8,1.25 2.8,2.8 0,1.54 -1.25,'
    +'2.8 -2.8,2.8 l 0,0 z"/>'
    +'</svg>';
var captions_icon_svg = '<svg viewBox="-12 -12 72 72">'
    +'<path d="M40 8H8c-2.21 0-4 1.79-4 4v24c0 2.21 1.79 4 4 4h32c2.21 0 '
    +'4-1.79 4-4V12c0-2.21-1.79-4-4-4zM8 24h8v4H8v-4zm20 12H8v-4h20v4zm12 '
    +'0h-8v-4h8v4zm0-8H20v-4h20v4z"/>'
    +'</svg>';
function extend_component(name, parent, comp){
    vjs.registerComponent(name, vjs.extend(vjs.getComponent(parent), comp));
    return vjs.getComponent(name);
}
var Menu = vjs.getComponent('Menu');
extend_component('PopupMenu', 'Menu', {
    className: 'vjs-rightclick-popup',
    popped: false,
    constructor: function(player, options){
        Menu.call(this, player, options);
        var player_ = player;
        this.addClass(this.className);
        this.hide();
        var _this = this;
        var opt = this.options_;
        _this.menuEnabled = true;
        this.addChild(new PoweredBy(player, {label:
            'Powered by Spark Player'}));
        if (opt.copy_url!==false)
        {
            this.addChild(new CopyUrlButton(player, {label: 'Copy video URL',
                url: opt.copy_url}));
        }
        if (opt.copy_url!==false && opt.copy_url_with_time!==false &&
            (!multiple_players()||opt.copy_url))
        {
            this.addChild(new CopyUrlButton(player, {url: opt.copy_url,
                label: 'Copy video URL at current time', time: true}));
        }
        if (opt.embed_code)
        {
            this.addChild(new CopyButton(player, {label: 'Copy embed code',
                text: opt.embed_code}));
        }
        if (opt.debugging)
        {
            this.addChild(new LogButton(player, {label: 'Download log'}));
            this.addChild(
                new CopyLogButton(player, {label: 'Copy debug info'}));
        }
        if (opt.report)
        {
            opt.report = vjs.mergeOptions({label: 'Report playback issue'},
                opt.report);
            this.addChild(new ReportButton(player, opt.report));
        }
        if (opt.info)
        {
            opt.info = vjs.mergeOptions({label: 'Stats for nerds'}, opt.info);
            this.addChild(new InfoButton(player, opt.info));
        }
        if (opt.graph)
        {
            opt.graph = vjs.mergeOptions({label: 'CDN overlay'}, opt.graph);
            this.addChild(new GraphButton(player, opt.graph));
        }
        if (opt.about)
        {
            this.addChild(new MenuItemLink(player, {
                href: 'https://holaspark.com/player',
                label: 'About Spark Player',
            }));
        }
        function get_overflow_parent(el){
            var parent = el;
            while (parent = parent.parentElement)
            {
                if (!parent)
                    return;
                var style = window.getComputedStyle(parent);
                if (style.overflowX!='visible' || style.overflowY!='visible')
                    return parent;
            }
        }
        function oncontextmenu(evt){
            evt.preventDefault();
            if (_this.popped)
                return void _this.hide();
            _this.show();
            _this.check_items();
            var el = _this.el(), x = evt.clientX, y = evt.clientY;
            var max_right = window.innerWidth;
            var max_bottom = window.innerHeight;
            var parent = get_overflow_parent(el);
            if (parent)
            {
                var parent_rect = parent.getBoundingClientRect();
                max_right = Math.min(max_right, parent_rect.right);
                max_bottom = Math.min(max_bottom, parent_rect.bottom);
            }
            var left_shift = x+el.offsetWidth-max_right+5;
            left_shift = Math.max(0, left_shift);
            var top_shift = y+el.offsetHeight-max_bottom+5;
            top_shift = Math.max(0, top_shift);
            var rect = _this.player().el().getBoundingClientRect();
            el.style.left = Math.max(0, x-rect.left-left_shift)+'px';
            el.style.top = Math.max(0, y-rect.top-top_shift)+'px';
        }
        player_.on('contextmenu', oncontextmenu);
        player_.on(['tap', 'click'], function(evt){
            if (_this.popped)
            {
                _this.hide();
                evt.stopPropagation();
                evt.preventDefault();
                return false;
            }
        });
        vjs.on(document, ['tap', 'click'], function(){
            if (_this.popped)
                _this.hide();
        });
        player_.on('hola.wrapper_attached', this.check_items.bind(this));
        player_.on('hola.wrapper_detached', this.check_items.bind(this));
        this.children().forEach(function(item){
            item.on(['tap', 'click'], function(){ _this.hide(); });
        });
        player.enablePopupMenu = function(){
            if (!_this.menuEnabled)
            {
                player_.off('contextmenu');
                player_.on('contextmenu', oncontextmenu);
                _this.menuEnabled = true;
            }
        };
        player.disablePopupMenu = function(){
            if (_this.menuEnabled)
            {
                player_.off('contextmenu');
                player_.on('contextmenu', function(evt){
                    evt.preventDefault(); });
                _this.menuEnabled = false;
            }
        };
    },
    createEl: function() {
        this.contentEl_ = vjs.createEl('ul', {
            className: 'vjs-menu-content'
        });
        this.contentEl_.setAttribute('role', 'menu');
        var el = Component.prototype.createEl('div', {
            append: this.contentEl_,
            className: 'vjs-menu',
        });
        el.setAttribute('role', 'presentation');
        el.appendChild(this.contentEl_);
        var _this = this;
        vjs.on(el, 'click', function(event){
            _this.hide();
            event.preventDefault();
            event.stopImmediatePropagation();
        });
        return el;
    },
    show: function(){
        this.removeClass('vjs-hidden');
        this.popped = true;
    },
    hide: function(){
        this.addClass('vjs-hidden');
        this.popped = false;
    },
    check_items: function(){
        this.children().forEach(function(item){
            if (item.is_visible)
                item.toggleClass('vjs-hidden', !item.is_visible());
        });
    },
});
var SubMenu = extend_component('SubMenu', 'Menu', {
    addToMain: true,
    item_count: 17,
    constructor: function(player, options, parent){
        var picker_enabled = true;
        if (options&&options.picker!==undefined)
            picker_enabled = options.picker;
        this.picker_mode_wanted = picker_enabled &&
            player&&player.hasClass('vjs-ios-skin');
        this.picker_mode = this.picker_mode_wanted;
        if (!this.picker_mode)
            this.getHeight = undefined;
        this.items = [];
        this.picker_items = [];
        this.parent = parent;
        this.line_height = options&&options.line_height||31;
        this.radius = 60;
        this.radius_ratio = this.radius/this.item_count;
        Menu.call(this, player, options);
        this.createMenuItem();
        this.createTitleItem();
        if (this.className)
            this.addClass(this.className);
        this.update();
        this.handleTouch();
    },
    createEl: function(){
        var class_name = 'vjs-menu-content';
        if (this.picker_mode)
            class_name += ' vjs-picker';
        var el = Component.prototype.createEl.call(this, 'div',
            {className: class_name});
        el.setAttribute('role', 'menu');
        this.ul = Component.prototype.createEl('ul',
            {className: 'vjs-menu-submenu'});
        if (this.picker_mode)
        {
            this.wrapper = Component.prototype.createEl('div', {
            className: 'vjs-picker-wrapper'});
            var container = Component.prototype.createEl('div', {
                className: 'vjs-picker-container'});
            container.appendChild(this.ul);
            this.wrapper.appendChild(container);
            el.appendChild(this.wrapper);
            return el;
        }
        el.appendChild(this.ul);
        return el;
    },
    addItem: function(component){
        Menu.prototype.addItem.call(this, component);
        this.ul.appendChild(component.el_);
    },
    createTitleItem: function(){
        if (!this.title)
            return;
        var _this = this;
        var title = new MenuItem(this.player_, {label: this.title});
        title.addClass('vjs-submenu-title');
        title.on(['tap', 'click'], function(){
            _this.parent.back();
        });
        this.addChild(title);
        this.titleItem = title;
    },
    createMenuItem: function(){
        if (!this.title || !this.addToMain)
            return;
        var player = this.player(), _this = this;
        var item = this.menuItem = new MenuItem(player, {label: this.title});
        item.addClass('vjs-menu-item-next');
        var span = vjs.createEl('span', {className: 'vjs-menu-item-content'});
        item.minorLabel = vjs.createEl('span', {className: 'vjs-minor-label'});
        item.contentLabel = vjs.createEl('span');
        span.appendChild(item.contentLabel);
        span.appendChild(item.minorLabel);
        item.el().insertBefore(span, item.el().firstChild);
        item.on(['tap', 'click'], function(){
            _this.parent.next(_this); });
    },
    getHeight: function(){
        var title_offset = this.titleItem ?
            this.titleItem.el_.offsetHeight : 0;
        var max_height = get_max_height(this.player_)-title_offset;
        return Math.min(max_height, this.picker ? 90 :
            (this.items.length||this.children_.length*this.line_height));
    },
    insert_item: function(item){
        var angle = item.angle;
        item = item.item||item;
        var handle_item = item;
        if (this.picker_mode)
        {
            handle_item = new MenuItem(this.player_,
                vjs.mergeOptions({}, item.options_));
            handle_item.el_.style.transform = 'rotateX('+(-angle)+
                'deg) translateZ('+this.radius+'px)';
            handle_item.selected(item.hasClass('vjs-selected'));
            this.picker_items.push(handle_item);
        }
        this.addChild(handle_item);
        this.ul.appendChild(handle_item.el_);
        var _this = this;
        handle_item.on(['tap', 'click'], function(e){
            e.stopImmediatePropagation();
            if (_this.handleItemClick)
                _this.handleItemClick(item);
            if (_this.picker_mode)
                _this.update();
        });
    },
    // XXX alexeym: cleanup all picker code
    update: function(){
        var _this = this;
        this.items.forEach(function(item){
            _this.removeChild(item);
            if (item.el_.parentNode)
                item.el_.parentNode.removeChild(item.el_);
        });
        this.items = [];
        this.picker_items.forEach(function(child){
            child.dispose&&child.dispose(); });
        this.picker_items = [];
        if (this.createItems)
            this.createItems();
        var items_count = this.items.length;
        if (!items_count||items_count<2)
            this.picker_mode = false;
        else
            this.picker_mode = this.picker_mode_wanted;
        if (!this.picker_mode)
            return this.items.forEach(this.insert_item.bind(this));
        var ul_height = this.getHeight();
        this.wrapper.style.height = ul_height+'px';
        this.item_count = items_count * (items_count<4 ? 3 : 2);
        var radius = this.radius = this.radius_ratio*this.item_count;
        var selected = this.items.filter(function(item){
            return item.hasClass('vjs-selected'); })[0];
        selected = selected ? this.items.indexOf(selected) : -1;
        if (selected==-1)
            selected = 0;
        this.theta = 360/this.item_count;
        this.drum_rotate = selected*this.theta;
        this.ul.style.transform = 'translateZ(-'+radius+
            'px) rotateX('+this.drum_rotate+'deg)';
        var visible = [];
        for (var i=0, index=0; i<this.item_count;i++)
        {
            var offset = Math.floor(i/items_count)*items_count;
            var data_index = i-offset;
            var item = this.items[data_index];
            var angle = this.theta * index;
            visible.push({item: item, angle: angle});
            index++;
        }
        visible.forEach(this.insert_item.bind(this));
    },
    handleTouch: function(){
        var _this = this, position = null;
        var step_height = this.line_height*get_ui_zoom(this.player_);
        this.on('touchstart', function(event){
            position = null;
            if (!_this.picker_mode||!_this.ul||!event.touches.length||
                event.srcElement.parentNode===_this.el)
            {
                return;
            }
            position = event.touches[0].pageY;
            _this.addClass('vjs-rotate-transition');
        });
        this.on('touchmove', function(event){
            if (!_this.picker_mode||!_this.ul||position===null)
                return;
            event.preventDefault();
            event.stopPropagation();
            var ydiff = event.touches[0].pageY - position;
            var steps = Math.trunc(ydiff/step_height);
            if (!steps)
                return;
            position = event.touches[0].pageY;
            // limit scrolling to 1 by event to avoid skipping menu items
            _this.drum_rotate -= (steps<0 ? -1 : 1)*_this.theta;
            _this.ul.style.transform = 'translateZ(-'+_this.radius+
                'px) rotateX('+_this.drum_rotate+'deg)';
        });
        var stop_touch = function(){
            if (position===null)
                return;
            _this.removeClass('vjs-rotate-transition');
            var index = _this.get_rotate_index();
            _this.picker_items[index].trigger('tap');
            position = null;
        };
        this.on('touchend', stop_touch);
        this.on('touchcancel', stop_touch);
        this.on('touchleave', stop_touch);
    },
    get_rotate_index: function(){
        var index = Math.round(this.drum_rotate / this.theta);
        var offset = Math.trunc(index/this.item_count)*this.item_count;
        index = index-offset;
        return index<0 ? this.item_count+index : index;
    },
});
var QualitySubMenu = extend_component('QualitySubMenu', 'SubMenu', {
    className: 'vjs-quality-submenu',
    title: 'Quality',
    picker: true,
    constructor: function(player, options, parent){
        var _this = this, tech = player.tech_;
        SubMenu.call(this, player, options, parent);
        var quality = this.options_.quality;
        var sources = quality && quality.sources ? quality.sources :
            player.options_.sources;
        if (sources&&sources.length>1)
        {
            var def_source = sources.filter(function(source){
                return source['default']; })[0];
            if (def_source&&def_source.src)
                this.player_.src(def_source.src);
        }
        this.one(player, 'play', this.updateSelected);
        this.on(player, 'resolutionchange', this.updateSelected);
        this.updateSelected();
        if (tech)
        {
            tech.on('loadedqualitydata', function(e, data){
                _this.updateQuality(data); });
            if (tech.quality_data)
                this.updateQuality(tech.quality_data);
        }
    },
    createItems: function(){
        var player = this.player();
        var quality = this.options_.quality;
        var sources = quality && quality.sources ? quality.sources :
            player.options_.sources;
        if (quality===false || !sources || sources.length<2)
            return void this.menuItem.hide();
        this.menuItem.show();
        for (var i=0; i<sources.length; i++)
        {
            var label = sources[i].label || (sources.length==1 ?
                'Auto' : this.localize('Source')+' '+(i+1));
            var item = new QualityMenuItem(player, vjs.mergeOptions(
                sources[i], {label: label, callback: quality.callback}));
            this.items.push(item);
        }
        this.updateSelected();
    },
    handleItemClick: function(item){
        var player = this.player();
        if (!this.picker_mode)
            this.parent.settings_button.unpressButton();
        if (item.hasClass('vjs-selected'))
            return;
        var quality = item.options_;
        var level_id = quality.level_id;
        var event, event_name = 'beforeresolutionchange';
        try { event = new window.CustomEvent(event_name); }
        catch(e){
            // XXX stanislav: IE 11 fix
            event = document.createEvent('CustomEvent');
            event.initCustomEvent(event_name, true, true, {});
        }
        player.trigger(event, quality);
        if (event.defaultPrevented)
            return;
        if (level_id!==undefined)
        {
            if (quality.callback)
                quality.callback(level_id);
            player.trigger('resolutionchange');
            return;
        }
        var current_time = player.currentTime();
        var remain_paused = player.paused();
        player.pause();
        player.src(quality.src);
        player.ready(function(){
            seek(player, current_time);
            player.trigger('resolutionchange');
            if (!remain_paused)
            {
                player.load();
                player.play();
            }
        });
    },
    updateSelected: function(){
        var _this = this;
        var player = this.player_;
        var current_src = player.cache_.src;
        var hola = window.hola_cdn;
        var wrapper = hola && hola.wrapper && find(hola.wrapper, function(w){
            return w.player && w.player.vjs==player;
        });
        // in case of hola_cdn attached get origin video url instead of blob
        if (wrapper && wrapper.player)
            current_src = wrapper.player.get_url();
        var items = this.items, selected_label;
        items.forEach(function(item){
            var selected = item.options_.src ?
                cmp_url(item.options_.src, current_src) :
                item.options_.level_id==_this.selectedLevel;
            item.selected(selected);
            if (selected)
                selected_label = item.options_.label;
        });
        var current_label;
        if (this.selectedLevel==-1 && this.currentLevel!==undefined)
        {
            var levels = (this.options_.quality||{}).sources||[];
            var cur = find(levels, function(l){
                return l.level_id==_this.currentLevel; });
            current_label = cur ? cur.label : '';
        }
        var type = quality_type(current_label || selected_label);
        this.menuItem.contentLabel.innerHTML =
            this.localize(selected_label)||'';
        this.menuItem.minorLabel.innerHTML = this.localize(current_label)||'';
        this.menuItem.toggleClass('vjs-quality-hd', type=='hd');
        this.menuItem.toggleClass('vjs-quality-4k', type=='4k');
        this.parent.settings_button.toggleClass('vjs-quality-hd', type=='hd');
        this.parent.settings_button.toggleClass('vjs-quality-4k', type=='4k');
    },
    levelsChanged: function(levels){
        var current = (this.options_.quality||{}).sources||[];
        if (current.length!=levels.length)
            return true;
        for (var i=0; i<levels.length; i++)
        {
            if (current[i].level_id!=levels[i].id)
                return true;
        }
        return false;
    },
    updateQuality: function(data){
        var sources = [];
        var callback = data.callback;
        var levels = data.quality.list.slice().sort(function(a, b){
            return b.id==-1 ? -1 : a.id==-1 ? 1 : b.bitrate-a.bitrate; });
        this.selectedLevel = data.quality.selected;
        this.currentLevel = data.quality.current;
        var quality = this.options_.quality = this.options_.quality||{};
        if (this.levelsChanged(levels) || callback!=quality.callback)
        {
            levels.forEach(function(item){
                sources.push({level_id: item.id, label: item.label});
            });
            quality.sources = sources;
            quality.callback = callback;
            this.update();
        }
        this.updateSelected();
    }
});
function quality_type(label){
    var m = label && label.match(/(\d+)p/);
    var q = m && parseInt(m[1], 10);
    return q>=2160 ? '4k' : q>=720 ? 'hd' : null;
}
function is_hls_provider(player){
    // XXX bahaa/alexeym: make it an opt instead of detecting provider
    return player.tech_ && (player.tech_.flashlsProvider ||
        player.tech_.hlsProvider);
}
var QualityMenuItem = extend_component('QualityMenuItem', 'MenuItem', {
    constructor: function(player, options){
        options = vjs.mergeOptions({selectable: true}, options);
        MenuItem.call(this, player, options);
        var qt;
        if (qt = quality_type(options.label))
            this.addClass('vjs-quality-'+qt);
    },
    handleClick: function(){},
});
var SpeedSubMenu = extend_component('SpeedSubMenu', 'SubMenu', {
    className: 'vjs-speed-submenu',
    title: 'Speed',
    values: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2],
    picker: true,
    constructor: function(player, options, parent){
        this.supported = player.tech_ && player.tech_.featuresPlaybackRate;
        SubMenu.call(this, player, options, parent);
        this.on(player, 'ratechange', this.handleRateChange);
        var rate = parseFloat(local_storage_get('vjs5_speed'));
        if (this.supported && this.values.indexOf(rate)!=-1)
            player.playbackRate(rate);
        this.handleRateChange();
    },
    createItems: function(){
        var _this = this, player = this.player();
        var rates = this.supported ? this.values : [1];
        rates.forEach(function(rate){
            var item = new MenuItem(player, {
                label: rate==1 ? 'Normal' : rate,
                selectable: true,
                rate: rate,
            });
            _this.items.push(item);
        });
        this.handleRateChange();
    },
    handleItemClick: function(item){
        var player = this.player(), rate = item.options_.rate;
        if (!this.picker_mode)
            this.parent.back();
        if (rate!=player.playbackRate())
            player.playbackRate(rate);
        local_storage_set('vjs5_speed', rate);
    },
    handleRateChange: function(){
        var rate = this.player().playbackRate();
        this.menuItem.contentLabel.innerHTML = rate==1 ?
            this.localize('Normal') : rate;
        this.items.forEach(function(item){
            item.selected(item.options_.rate==rate);
        });
    },
});
function get_captions_tracks(player){
    var tracks = [];
    var tt = player.textTracks();
    if (!tt || !tt.length)
        return tracks;
    for (var i=0; i<tt.length; i++)
    {
        if (tt[i].kind=='subtitles' || tt[i].kind=='captions')
            tracks.push(tt[i]);
    }
    return tracks;
}
function get_track_label(track){
    return track ? track.label||track.language||track.kind : 'Off';
}
var CaptionsSubMenu = extend_component('CaptionsSubMenu', 'SubMenu', {
    picker: true,
    className: 'vjs-captions-submenu',
    title: 'Subtitles/CC',
    constructor: function(player, options, parent){
        SubMenu.call(this, player, options, parent);
        var tt = player.textTracks();
        if (!tt || !tt.on)
            return;
        this.optionsMenu = new CaptionsOptionsMenu(player, options, parent);
        this.parent.addSubMenu(this.optionsMenu);
        var opt_el = vjs.createEl('div', {className: 'vjs-minor-label',
            innerHTML: this.localize('Options')});
        this.titleItem.el().appendChild(opt_el);
        this.on(opt_el, ['touchstart', 'touchend', 'click'], function(event){
            event.preventDefault();
            event.stopPropagation();
            if (event.type=='click' || event.type=='touchend')
                parent.next(this.optionsMenu);
        });
        this.on(tt, 'addtrack', this.update);
        this.on(tt, 'removetrack', this.update);
        this.on(tt, 'change', this.handleTrackChange);
    },
    createItems: function(){
        var player = this.player();
        var tracks = get_captions_tracks(player);
        if (!tracks.length)
            return void this.menuItem.hide();
        this.menuItem.show();
        for (var i=-1; i<tracks.length; i++)
        {
            var track = tracks[i];
            var item = new MenuItem(player, {
                label: get_track_label(track),
                selectable: true,
                track: track,
            });
            this.items.push(item);
        }
        this.handleTrackChange();
    },
    handleItemClick: function(item){
        var player = this.player();
        var tt = player.textTracks();
        for (var i=0; i<tt.length; i++)
            tt[i].mode = tt[i]==item.options_.track ? 'showing' : 'disabled';
        if (!this.picker_mode)
            this.parent.back();
        this.handleTrackChange();
    },
    handleTrackChange: function(){
        var selected = find(this.items, function(item){
            return item.options_.track && item.options_.track.mode=='showing';
        }) || find(this.items, function(item){ return !item.options_track; });
        this.items.forEach(function(item){ item.selected(item==selected); });
        this.menuItem.contentLabel.innerHTML =
            selected ? selected.options_.label : '';
    },
});
var CaptionsOptionsMenu = extend_component('CaptionsOptionsMenu', 'SubMenu', {
    className: 'vjs-captions-options-submenu',
    title: 'Options',
    addToMain: false,
    dict: {
        font: [
            {value: 'monospaceSerif', text: 'Monospace Serif'},
            {value: 'proportionalSerif', text: 'Proportional Serif'},
            {value: 'monospaceSansSerif', text: 'Monospace Sans-Serif'},
            {value: 'proportionalSansSerif', text: 'Proportional Sans-Serif'},
            {value: 'casual', text: 'Casual'},
            {value: 'script', text: 'Script'},
            {value: 'smallcaps', text: 'Small Caps'}
        ],
        size: [
            {value: '0.50', text: '50%'},
            {value: '0.75', text: '75%'},
            {value: '1.00', text: '100%'},
            {value: '1.50', text: '150%'},
            {value: '2.00', text: '200%'},
            {value: '3.00', text: '300%'},
            {value: '4.00', text: '400%'}
        ],
        opacity: [
            {value: '0.00', text: '0%'},
            {value: '0.25', text: '25%'},
            {value: '0.50', text: '50%'},
            {value: '0.75', text: '75%'},
            {value: '1.00', text: '100%'}
        ],
        color: [
            {value: '#FFF', text: 'White'},
            {value: '#000', text: 'Black'},
            {value: '#F00', text: 'Red'},
            {value: '#0F0', text: 'Green'},
            {value: '#00F', text: 'Blue'},
            {value: '#FF0', text: 'Yellow'},
            {value: '#F0F', text: 'Magenta'},
            {value: '#0FF', text: 'Cyan'}
        ],
        edge: [
            {value: 'none', text: 'None'},
            {value: 'raised', text: 'Raised'},
            {value: 'depressed', text: 'Depressed'},
            {value: 'uniform', text: 'Uniform'},
            {value: 'dropshadow', text: 'Drop shadow'}
        ],
    },
    constructor: function(player, options, parent){
        var d = this.dict;
        this.params = [
            {key: 'fontFamily', text: 'Font family', dict: d.font, def: 3},
            {key: 'color', text: 'Font color', dict: d.color, def: 0},
            {key: 'fontPercent', text: 'Font size', dict: d.size, def: 2},
            {key: 'textOpacity', text: 'Font opacity', dict: d.opacity,
                def: 4},
            {key: 'backgroundColor', text: 'Background color', dict: d.color,
                def: 1},
            {key: 'backgroundOpacity', text: 'Background opacity',
                dict: d.opacity, def: 3},
            {key: 'windowColor', text: 'Window color', dict: d.color, def: 1},
            {key: 'windowOpacity', text: 'Window opacity', dict: d.opacity,
                def: 0},
            {key: 'edgeStyle', text: 'Text edge style', dict: d.edge, def: 0}
        ];
        this.reset();
        this.load();
        SubMenu.call(this, player, options, parent);
        this.selectMenu = new SelectValueMenu(player, options, parent);
        this.on(this.selectMenu, 'selected', this.handleValueChange);
        this.parent.addSubMenu(this.selectMenu);
        var orig = player.getChild('textTrackSettings');
        player.removeChild(orig);
        orig.dispose();
        player.textTrackSettings = this;
    },
    save: function(){
        local_storage_set('vjs5_captions_options',
            JSON.stringify(this.getValues()));
    },
    load: function(){
        var values;
        try {
            values = JSON.parse(local_storage_get('vjs5_captions_options'));
        } catch(e){}
        if (!values)
            return;
        this.params.forEach(function(p){
            var value = find(p.dict, function(d){
                return d.value==values[p.key];
            });
            if (value)
                p.value = value;
        });
    },
    reset: function(){
        this.params.forEach(function(p){ p.value = p.dict[p.def]; });
    },
    getValues: function(){
        var res = {};
        this.params.forEach(function(p){ res[p.key] = p.value.value; });
        return res;
    },
    createItems: function(){
        var player = this.player, items = this.items;
        this.params.forEach(function(p){
            var item = new MenuItem(player, {label: p.text});
            item.param = p;
            item.addClass('vjs-menu-item-next');
            var span = vjs.createEl('span', {
                className: 'vjs-menu-item-content',
                innerHTML: item.localize(p.value.text),
            });
            item.contentLabel = span;
            item.el().insertBefore(span, item.el().firstChild);
            items.push(item);
        });
        items.push(new MenuItem(player, {label: 'Reset', reset: true}));
    },
    handleItemClick: function(item){
        if (item.options_.reset)
        {
            this.reset();
            this.update();
            this.changed();
            return;
        }
        this.currentItem = item;
        var p = item.param;
        this.selectMenu.show({title: p.text, dict: p.dict, selected: p.value});
    },
    handleValueChange: function(event, value){
        this.currentItem.param.value = value;
        this.currentItem.contentLabel.innerHTML = value.text;
        this.changed();
    },
    changed: function(){
        this.save();
        var d = this.player().textTrackDisplay;
        d.updateDisplay();
        if (this.timeout)
            this.clearTimeout(this.timeout);
        if (d.el().textContent)
            return;
        var text = this.localize('Subtitles will look like this');
        var fake_track = {activeCues: [new vtt.VTTCue(0, 0, text)]};
        d.updateForTrack(fake_track);
        this.timeout = this.setTimeout(function(){ d.updateDisplay(); }, 3000);
    },
});
var SelectValueMenu = extend_component('SelectValueMenu', 'SubMenu', {
    className: 'vjs-select-value-submenu',
    addToMain: false,
    dict: [],
    show: function(opt){
        this.title = opt.title;
        if (this.titleItem)
            this.removeChild(this.titleItem);
        this.createTitleItem();
        this.dict = opt.dict;
        this.update();
        this.value_selected = opt.selected;
        this.updateSelected();
        this.parent.next(this);
    },
    createItems: function(){
        var items = this.items, player = this.player();
        this.dict.forEach(function(d){
            var item = new MenuItem(player, {label: d.text, selectable: true});
            item.value = d;
            items.push(item);
        });
        this.updateSelected();
    },
    updateSelected: function(){
        this.items.forEach(function(i){
            i.selected(i.value==this.value_selected); });
    },
    handleItemClick: function(item){
        this.trigger('selected', item.value);
        this.value_selected = item.value;
        this.updateSelected();
    },
});
function need_invert(){
    var screen = window.screen;
    if (!screen)
        return false;
    // ios safari always use values for screen.avail*
    // from vertical rotation while android chrome change it
    // depending on the orientation
    var s = screen.availHeight > screen.availWidth;
    var w = window.innerHeight > window.innerWidth;
    return s!=w;
}
// XXX alexeym: unite zoom api with vjs5-hola-skin plugin
var get_ui_zoom = function(player){
    function clamp(zoom){
        // XXX alexeym: disable zoom < 1 because of wrong handling
        // for some elements
        return Math.max(1, zoom);
    }
    var scale = 1;
    if (player&&!player.hasClass('vjs-ios-skin'))
        return scale;
    var viewport = window.visualViewport;
    if (viewport&&viewport.scale)
        return clamp(1/viewport.scale);
    var screen = window.screen;
    if (!screen)
        return clamp(scale);
    var width_available = need_invert() ? screen.availHeight :
        screen.availWidth;
    if (width_available)
        scale = window.innerWidth/width_available;
    return clamp(scale);
};
var get_max_height = function(player){
    var ui_zoom = get_ui_zoom(player);
    var is_ios_skin = player&&player.hasClass('vjs-ios-skin');
    // XXX alexeym: get rid of magic numbers
    var offset = is_ios_skin ? 46 : 100;
    return (player.el().offsetHeight)/ui_zoom - offset;
};
var SettingsMenu = extend_component('SettingsMenu', 'Menu', {
    className: 'vjs-settings-menu',
    history: [],
    constructor: function(player, options, settings_button){
        Menu.call(this, player, options);
        this.settings_button = settings_button;
        this.addClass(this.className);
        this.update();
        this.on(['tap', 'click', 'touchstart', 'touchend'], function(event){
            event.stopPropagation();
        });
        var resize = this._resize = this.resize.bind(this);
        var zoom = this._touch_zoom = this.touch_zoom.bind(this);
        this.scale = 1;
        player.on('resize', resize);
        player.on('fullscreenchange', function(){ setTimeout(resize); });
        window.addEventListener('resize', resize);
        window.addEventListener('orientationchange', resize);
        document.addEventListener('touchend', zoom);
    },
    dispose: function(){
        window.removeEventListener('resize', this._resize);
        window.removeEventListener('orientationchange', this._resize);
        document.removeEventListener('touchend', this._touch_zoom);
        if (this._zoom_bounce)
            this._zoom_bounce = clearTimeout(this._zoom_bounce);
        Menu.prototype.dispose.call(this);
    },
    createEl: function(){
        var el = Component.prototype.createEl.call(this, 'div',
            {className: 'vjs-menu'});
        el.setAttribute('role', 'presentation');
        el.style.zoom = get_ui_zoom(this.player_);
        return el;
    },
    update: function(){
        this.children().forEach(this.removeChild.bind(this));
        this.createItems();
    },
    addSubMenu: function(menu){
        this.addChild(menu);
        if (menu.menuItem)
            this.mainMenu.addItem(menu.menuItem);
    },
    createItems: function(){
        this.mainMenu = new SubMenu(this.player_,
            vjs.mergeOptions({picker: false}, this.options_), this);
        this.mainMenu.addClass('vjs-main-submenu');
        this.addChild(this.mainMenu);
        var menus = [SpeedSubMenu, CaptionsSubMenu, QualitySubMenu];
        for (var i=0; i<menus.length; i++)
            this.addSubMenu(new menus[i](this.player_, this.options_, this));
        this.selectMain(true);
    },
    selectMain: function(no_transition){
        this.history = [];
        this.setActive(this.mainMenu, no_transition);
    },
    show: function(visible){
        if (visible)
        {
            this.el_.style.height = '';
            this.el_.style.width = '';
            this.selectMain(true);
            this.addClass('vjs-lock-showing');
            return;
        }
        var _this = this;
        this.el_.style.opacity = '0';
        this.setTimeout(function(){
            this.el_.style.opacity = '';
            _this.removeClass('vjs-lock-showing');
        }, 100);
    },
    getSize: function(el){
        el = el||this.el_;
        return {width: el.offsetWidth, height: el.offsetHeight};
    },
    setSize: function(size){
        this.el_.style.height = size ? size.height+'px' : '';
        this.el_.style.width = size ? size.width+'px' : '';
    },
    resize: function(){
        var ui_zoom = get_ui_zoom(this.player_);
        this.el_.style.zoom = ui_zoom;
        if (this.active)
            this.setActive(this.active);
    },
    touch_zoom: function(e){
        var scale = get_ui_zoom(this.player_);
        if (this._zoom_bounce)
            clearTimeout(this._zoom_bounce);
        if (this.scale==scale)
        {
            // XXX alexeym: hack for animation when zoom via double-tap
            if (e)
                this._zoom_bounce = setTimeout(this._touch_zoom, 500);
            return;
        }
        var zooming_out = this.scale<scale;
        this.scale = scale;
        this.resize();
        // XXX alexeym: hack for bounce-on-zoom animation when zooming out
        if (zooming_out)
            this._zoom_bounce = setTimeout(this._resize, 500);
    },
    setActive: function(menu, no_transition){
        if (!no_transition && window.requestAnimationFrame)
        {
            var menu_el = menu.el();
            var ui_zoom = get_ui_zoom(this.player_);
            this.el_.style.zoom = ui_zoom;
            var title_offset = menu.titleItem ?
                this.getSize(menu.titleItem.el_).height : 0;
            var max_height = get_max_height(this.player_)-title_offset;
            menu.ul.style.maxHeight = max_height+'px';
            var _this = this;
            var new_size = this.getSize(menu_el);
            if (menu.getHeight)
                new_size.height = menu.getHeight()+title_offset;
            this.setSize(this.getSize());
            window.requestAnimationFrame(function(){
                _this.addClass('vjs-size-transition');
                window.requestAnimationFrame(function(){
                    var on_end = function(){
                        _this.removeClass('vjs-size-transition');
                        _this.setSize();
                        _this.clearTimeout(timeout);
                    };
                    _this.setSize(new_size);
                    _this.one('transitionend', on_end);
                    var timeout = _this.setTimeout(on_end, 300);
                });
            });
        }
        this.active = menu;
        if (menu.update)
            menu.update();
        this.children().forEach(function(item){
            item.toggleClass('vjs-active-submenu', item==menu);
        });
    },
    next: function(menu){
        this.history.push(this.active);
        this.setActive(menu);
    },
    back: function(){
        this.setActive(this.history.pop() || this.mainMenu);
    }
});
var MenuButton = vjs.getComponent('MenuButton');
extend_component('SettingsButton', 'MenuButton', {
    controlText_: 'Settings',
    update: function(){
        var player = this.player_;
        var menu = new SettingsMenu(player, this.options_, this);
        if (this.menu)
            player.removeChild(this.menu);
        this.menu = menu;
        player.addChild(menu);
        this.buttonPressed_ = false;
        this.el_.setAttribute('aria-expanded', 'false');
        if (this.items && !this.items.length)
            this.hide();
        else if (this.items && this.items.length>1)
            this.show();
    },
    createEl: function(){
        var el = MenuButton.prototype.createEl.call(this);
        this.icon_ = vjs.createEl('div', {className: 'vjs-button-icon',
            innerHTML: settings_icon_svg});
        el.insertBefore(this.icon_, el.firstChild);
        return el;
    },
    buildCSSClass: function(){
        return MenuButton.prototype.buildCSSClass.call(this)+
            ' vjs-settings-button';
    },
    handleClick: function(){
        if (this.buttonPressed_)
            this.unpressButton();
        else
            this.pressButton();
    },
    updateState: function(){
        this.player_.toggleClass('vjs-settings-expanded', this.buttonPressed_);
        this.el_.setAttribute('aria-expanded', this.buttonPressed_);
        this.menu.show(this.buttonPressed_);
    },
    unpressButton: function(){
        if (!this.enabled_)
            return;
        this.buttonPressed_ = false;
        this.updateState();
        this.el_.focus();
        this.clearInterval(this.activityInterval);
        if (this.clickListener)
        {
            vjs.off(document, ['tap', 'click'], this.clickListener);
            this.player_.off(['tap', 'click'], this.clickListener);
            this.clickListener = null;
        }
    },
    pressButton: function(){
        if (!this.enabled_)
            return;
        this.buttonPressed_ = true;
        this.updateState();
        this.menu.focus();
        // prevent setting vjs-user-inactive when menu is opened
        this.activityInterval = this.setInterval(
            this.player_.reportUserActivity.bind(this.player_), 250);
        var _this = this;
        this.setTimeout(function(){
            _this.clickListener = _this.unpressButton.bind(_this);
            vjs.on(document, ['tap', 'click'], this.clickListener);
            _this.player_.on(['tap', 'click'], this.clickListener);
        });
    },
    tooltipHandler: function(){
        return this.icon_;
    },
});
var Component = vjs.getComponent('Component');
var Overlay = extend_component('Overlay', 'Component', {
    constructor: function(player, options){
        Component.call(this, player, options);
        this.hide();
    },
    createEl: function(type, props){
        var custom_class = this.options_['class'];
        custom_class = custom_class ? ' '+custom_class : '';
        var proto_component = Component.prototype;
        var container = proto_component.createEl.call(this, 'div',
            vjs.mergeOptions({className: 'vjs-info-overlay'+custom_class},
            props));
        this.createContent(container);
        return container;
    },
    createContent: function(){},
});
function round(val){
    if (typeof val!='number')
        return val;
    return val.toFixed(3);
}
function is_wrapper_attached(check_bws){
    var hola = window.hola_cdn;
    return hola && hola.get_wrapper() && (!check_bws || !!hola._get_bws());
}
extend_component('InfoOverlay', 'Overlay', {
    constructor: function(player, options){
        this.info_data = {
            duration: {
                units: 'sec',
                title: 'Duration',
                get: function(p){ return round(p.duration()); },
            },
            position: {
                units: 'sec',
                title: 'Position',
                get: function(p){
                    return round(p.currentTime());
                },
            },
            buffered: {
                units: 'sec',
                title: 'Current buffer',
                get: function(p){
                    var range = p.buffered();
                    var pos = p.currentTime();
                    if (range && range.length)
                    {
                        for (var i=0; i<range.length; i++)
                        {
                            if (range.start(i)<=pos && range.end(i)>=pos)
                                return round(range.end(i)-pos);
                        }
                    }
                    return '--';
                },
            },
            downloaded: {
                units: 'sec',
                title: 'Downloaded',
                get: function(p){
                    var range = p.buffered();
                    var buf_sec = 0;
                    if (range && range.length)
                    {
                        for (var i=0; i<range.length; i++)
                            buf_sec += range.end(i)-range.start(i);
                    }
                    return round(buf_sec);
                },
            },
        };
        Overlay.call(this, player, options);
    },
    createContent: function(container){
        var _this = this;
        var player = this.player_;
        function create_el(el, opt){
            opt = opt ? vjs.mergeOptions(opt) : opt;
            var proto_component = Component.prototype;
            return proto_component.createEl.call(_this, el, opt);
        }
        var title = create_el('div', {
            className: 'vjs-info-overlay-title',
            innerHTML: this.localize('Technical info'),
        });
        var close_btn = create_el('div', {className: 'vjs-info-overlay-x'});
        var close = this.toggle.bind(this, null, true);
        close_btn.addEventListener('click', close);
        close_btn.addEventListener('touchend', close);
        var content = create_el('div', {
            className: 'vjs-info-overlay-content'});
        var list = create_el('ul', {className: 'vjs-info-overlay-list'});
        var item;
        var title_text;
        for (var i in this.info_data)
        {
            item = create_el('li', {className: 'vjs-info-overlay-list-item'});
            title_text = this.localize(this.info_data[i].title);
            if (this.info_data[i].units)
                title_text += ' ['+this.localize(this.info_data[i].units)+']';
            title_text += ': ';
            item.appendChild(create_el('strong', {
                innerHTML: title_text}));
            this.info_data[i].el = create_el('span');
            item.appendChild(this.info_data[i].el);
            list.appendChild(item);
        }
        content.appendChild(list);
        container.appendChild(title);
        container.appendChild(close_btn);
        container.appendChild(content);
        this.update();
        player.on('timeupdate', function(){ _this.update(); });
        // force updates when player is paused
        this.updateInterval = setInterval(function(){ _this.update(); }, 1000);
    },
    update: function(){
        var player = this.player_;
        var info = this.info_data;
        for (var i in info)
            info[i].el.innerHTML = info[i].get(player);
    },
    toggle: function(caller, hide){
        if (caller)
            this.last_caller = caller;
        if (this.visible||hide)
        {
            this.visible = false;
            if (this.last_caller)
                this.last_caller.selected(false);
            this.addClass('vjs-hidden');
            return;
        }
        this.update();
        this.visible = true;
        this.removeClass('vjs-hidden');
    },
    dispose: function(){
        clearInterval(this.updateInterval);
        Overlay.prototype.dispose.call(this);
    },
});
extend_component('NotifyOverlay', 'Overlay', {
    constructor: function(player, options){
        Overlay.call(this, player, options);
        this.addClass('vjs-notify-overlay');
    },
    createContent: function(container){
        var _this = this;
        function create_el(el, opt){
            opt = opt ? vjs.mergeOptions(opt) : opt;
            var proto_component = Component.prototype;
            return proto_component.createEl.call(_this, el, opt);
        }
        var title = create_el('div', {
            className: 'vjs-notify-overlay-title',
            innerHTML: this.localize('Issue report sent'),
        });
        var content = create_el('div', {
            className: 'vjs-notify-overlay-content',
            innerHTML: this.localize('Thank you!'),
        });
        container.appendChild(title);
        container.appendChild(content);
    },
    flash: function(){
        if (this.visible)
            return;
        this.visible = true;
        this.removeClass('vjs-hidden');
        var _this = this;
        setTimeout(function(){
            _this.addClass('vjs-notify-flash');
        }, 50);
        setTimeout(function(){
            _this.visible = false;
            _this.removeClass('vjs-notify-flash');
            _this.addClass('vjs-hidden');
        }, 3500);
    },
});
var ClickableComponent = vjs.getComponent('ClickableComponent');
var MenuItem = vjs.getComponent('MenuItem');
MenuItem.prototype.createEl = function(type, props, attrs){
    props = Object.assign({
        className: 'vjs-menu-item',
        innerHTML: '<span class="vjs-menu-item-label">'+
            this.localize(this.options_.label)+'</span>',
        tabIndex: -1,
    }, props);
    return ClickableComponent.prototype.createEl('li', props, attrs);
};
var PoweredBy = extend_component('PoweredBy', 'MenuItem', {
    constructor: function(player, options){
        var ver = window.hola_player&&window.hola_player.VERSION;
        if (ver)
            options.label += ' '+ver;
        MenuItem.call(this, player, options);
        this.addClass('vjs-powered-by');
    },
});
var MenuItemLink = extend_component('MenuItemLink', 'MenuItem', {
    createEl: function(type, props){
        var prot = MenuItem.prototype;
        var label = this.localize(this.options_.label);
        var el = prot.createEl.call(this, 'li', vjs.mergeOptions({
            className: 'vjs-menu-item vjs-menu-item-link',
            innerHTML: '',
        }, props));
        this.link = Component.prototype.createEl('a', {
            className: 'vjs-menu-link',
            innerHTML: this.localize(label),
        }, {
            target: '_blank',
            href: this.options_.href||'#',
        });
        el.appendChild(this.link);
        this.link.addEventListener('touchstart', function(e){
            e.stopPropagation(); });
        return el;
    },
    handleClick: function(e){ e.stopPropagation(); },
});
var ReportButton = extend_component('ReportButton', 'MenuItem', {
    is_visible: is_wrapper_attached,
    handleClick: function(){
        this.player_.trigger({type: 'problem_report'});
        var overlay;
        if (overlay = this.player_.getChild('NotifyOverlay'))
            overlay.flash();
        this.selected(false);
    },
});
var LogButton = extend_component('LogButton', 'MenuItem', {
    is_visible: is_wrapper_attached,
    handleClick: function(){
        this.player_.trigger({type: 'save_logs'});
        this.selected(false);
    },
});
var GraphButton = extend_component('GraphButton', 'MenuItem', {
    is_visible: is_wrapper_attached.bind(null, true),
    handleClick: function(){
        this.player_.trigger({type: 'cdn_graph_overlay'});
        this.selected(false);
    },
});
var CopyButton = extend_component('CopyButton', 'MenuItem', {
    constructor: function(player, options){
        MenuItem.call(this, player, options);
        this.clipboard = new Clipboard(this.el_, {
            text: this.getText.bind(this),
        });
        this.on('tap', function(e){
            this.clipboard.onClick({currentTarget: e.target});
        });
    },
    getText: function(){
        return this.options_.text;
    },
    dispose: function(){
        this.clipboard.destroy();
        MenuItem.prototype.dispose.call(this);
    },
});
var CopyLogButton = extend_component('CopyLogButton', 'CopyButton', {
    getText: function(){
        var player = this.player();
        if (!player.hola_logs)
            return 'Can\'t find hola_logs method!';
        return player.hola_logs();
    },
    is_visible: is_wrapper_attached,
});
var CopyUrlButton = extend_component('CopyUrlButton', 'CopyButton', {
    getText: function(){
        var url = this.options_.url || get_top_url();
        if (this.options_.time)
        {
            var pos = Math.floor(this.player().currentTime());
            var re = /(#(?:.*&)?t=)(\d*)/;
            if (url.match(re))
                url = url.replace(re, '$1'+pos);
            else
                url += (url.indexOf('#')!=-1 ? '&t=' : '#t=')+pos;
        }
        return url;
    },
});
var InfoButton = extend_component('InfoButton', 'MenuItem', {
    handleClick: function(){
        var overlay;
        if (overlay = this.player_.getChild('InfoOverlay'))
            overlay.toggle(this);
    },
});
var Button = vjs.getComponent('Button');
extend_component('CaptionsToggle', 'Button', {
    controlText_: 'Subtitles/closed captions',
    constructor: function(player, options){
        Button.call(this, player, options);
        this.addClass('vjs-captions-toggle');
        this.hide();
        var tt = player.textTracks();
        if (!tt || !tt.on)
            return;
        this.on(tt, 'addtrack', this.update);
        this.on(tt, 'removetrack', this.update);
        this.on(tt, 'change', this.update);
        this.update();
    },
    createEl: function(){
        var el = Button.prototype.createEl.call(this);
        this.icon_ = vjs.createEl('div', {className: 'vjs-button-icon',
            innerHTML: captions_icon_svg});
        el.insertBefore(this.icon_, el.firstChild);
        return el;
    },
    handleClick: function(){
        if (!this.track)
            return;
        var enable = this.track.mode!='showing';
        this.track.mode = enable ? 'showing' : 'disabled';
        if (enable)
            this.showHint();
    },
    showHint: function(){
        var track;
        if (!(track = this.track))
            return;
        var d = this.player().textTrackDisplay, i;
        if (this.timeout)
            this.clearTimeout(this.timeout);
        var cues = [
            new vtt.VTTCue(0, 0, get_track_label(track)),
            new vtt.VTTCue(0, 0, this.localize('press %s to configure'))
        ];
        for (i=0; i<cues.length; i++)
        {
            cues[i].align = 'start';
            cues[i].position = 28;
            cues[i].line = 2+i;
        }
        for (i=0; i<track.activeCues.length; i++)
            cues.push(track.activeCues[i]);
        d.updateForTrack({activeCues: cues});
        var svg = settings_icon_svg.replace(/viewBox="[^"]*"/,
            'viewBox="6 6 24 24"');
        cues[1].displayState.innerHTML = cues[1].displayState.innerHTML
            .replace('%s', svg);
        this.timeout = this.setTimeout(function(){ d.updateDisplay(); }, 3000);
    },
    update: function(){
        var tracks = get_captions_tracks(this.player());
        if (tracks.length)
            this.show();
        else
            this.hide();
        if (this.track && tracks.indexOf(this.track)==-1)
            this.track = null;
        var current = find(tracks, function(t){ return t.mode=='showing'; });
        if (current)
            this.track = current;
        if (!this.track)
        {
            this.track = find(tracks, function(t){ return t['default']; }) ||
                tracks[0];
        }
        this.toggleClass('vjs-pressed',
            this.track && this.track.mode=='showing');
    },
});
function local_storage_set(key, value){
    try { vjs.utils.localStorage.setItem(key, value); } catch(e){}
}
function local_storage_get(key){
    try { return vjs.utils.localStorage.getItem(key); }
    catch(e){ return null; }
}
function get_top_url(){
    return window.top==window ? location.href : document.referrer;
}
function seek(player, pos){
    player.one('loadeddata', function(){
        if (player.techName_=='Html5')
            return void player.currentTime(pos);
        // XXX andrey: if seek immediately, video can stuck
        // or play without sound, probably loadeddata is triggered
        // when flash NetStream is not ready to seek yet
        player.on('timeupdate', function on_timeupdate(){
            if (!player.currentTime())
                return;
            player.off('timeupdate', on_timeupdate);
            player.currentTime(pos);
        });
    });
}
function multiple_players(){
    return Object.keys(vjs.getPlayers()).length>1;
}
function cmp_url(a, b){
    var re = /^https?\:\/\//i;
    return a.replace(re, '//')==b.replace(re, '//');
}

vjs.plugin('settings', function(opt){
    var video = this;
    opt = vjs.mergeOptions({
        info: true,
        report: true,
        quality: false,
        volume: {level: 1, mute: !!video.options_.muted},
        debugging: true,
        about: true,
    }, opt);
    video.ready(function(){
        function sources_normalize(sources, label_sav){
            var i, source_def, source_sav;
            sources = sources.filter(function(e){ return e.src; });
            for (i=0; i<sources.length; i++)
            {
                if (!sources[i].label)
                    sources[i].label = sources[i].type;
                if (!source_def && sources[i]['default']!==undefined)
                    source_def = sources[i];
                if (label_sav && label_sav==sources[i].label &&
                    sources[i]['prevent-default']===undefined)
                {
                    source_sav = sources[i];
                }
            }
            for (i=0; i<sources.length; i++)
            {
                sources[i]['default'] =
                    source_sav ? sources[i]===source_sav :
                    source_def ? sources[i]===source_def : false;
            }
            return sources;
        }
        if (opt.quality===true)
            opt.quality = {sources: video.options_.sources};
        if (opt.quality && !is_hls_provider(video))
        {
            var quality_key = 'vjs5_quality';
            opt.quality.sources = sources_normalize(opt.quality.sources,
                local_storage_get(quality_key));
            video.on('resolutionchange', function(){
                var sources = opt.quality.sources;
                for (var i=0; i<sources.length; i++)
                {
                    if (!cmp_url(video.currentSrc(), sources[i].src))
                        continue;
                    local_storage_set(quality_key, sources[i].label);
                    break;
                }
            });
        }
        video.controlBar.addChild('CaptionsToggle', vjs.mergeOptions(opt));
        video.controlBar.addChild('SettingsButton', vjs.mergeOptions(opt));
        if (opt.info)
            video.addChild('InfoOverlay');
        if (opt.report)
            video.addChild('NotifyOverlay');
        if (opt.volume)
        {
            var volume_key = 'vjs5_volume', mute_key = 'vjs5_mute';
            // quality configuration above might have reset the source
            // thus make sure video is ready before changing the volume
            video.ready(function(){
                // do not unmute the video if autoplay is enabled as browser
                // can pause it
                if (video.muted() && video.autoplay())
                    return;
                var ls_level, ls_mute;
                if ((ls_level = local_storage_get(volume_key))!=null)
                    opt.volume.level = ls_level;
                if ((ls_mute = local_storage_get(mute_key))!=null)
                    opt.volume.mute = ls_mute=='true';
                video.volume(opt.volume.level);
                video.muted(opt.volume.mute);
            });
            video.on('volumechange', function(){
                local_storage_set(volume_key, video.volume());
                local_storage_set(mute_key, video.muted());
            });
        }
        video.addChild('PopupMenu', vjs.mergeOptions(opt));
        var url = get_top_url(), m;
        if (!multiple_players() && (m = url.match(/#(?:.*&)?t=(\d*)/)))
            seek(video, parseInt(m[1], 10));
    });
});

}(window, window.videojs));

},{"@hola.org/videojs-utils":169,"clipboard":173,"lodash/find":143,"videojs-vtt.js":392}],168:[function(_dereq_,module,exports){
(function() {
  'use strict';
  var extend = function() {
      var args, target, i, object, property;
      args = Array.prototype.slice.call(arguments);
      target = args.shift() || {};
      for (i in args) {
        object = args[i];
        for (property in object) {
          if (object.hasOwnProperty(property)) {
            if (typeof object[property] === 'object') {
              target[property] = extend(target[property], object[property]);
            } else {
              target[property] = object[property];
            }
          }
        }
      }
      return target;
    },
    getComputedStyle = function(el, pseudo) {
      return function(prop) {
        if (window.getComputedStyle) {
          return window.getComputedStyle(el, pseudo)[prop];
        } else {
          return el.currentStyle[prop];
        }
      };
    },
    offsetParent = function(el) {
      if (el.nodeName !== 'HTML' && getComputedStyle(el)('position') === 'static') {
        return offsetParent(el.offsetParent);
      }
      return el;
    },
    getScrollOffset = function() {
      if (window.pageXOffset) {
        return {
          x: window.pageXOffset,
          y: window.pageYOffset
        };
      }
      return {
        x: document.documentElement.scrollLeft,
        y: document.documentElement.scrollTop
      };
    },
    // unfold sprites configuration by automatically calculating correct window
    // for each sprite image
    unfoldSpritesConf = function(options) {
      var last = {};
      Object.keys(options).forEach(function(key) {
        var s;
        if (!(s = options[key].sprites))
          return;
        delete options[key].sprites;
        if (!s.position && s.interval && s.count) {
          s.position = [];
          for (var i=0; i<s.count; i++)
            s.position.push(+key+i*s.interval);
        }
        var rows = s.rows||1;
        var cols = Math.ceil(s.position.length/rows);
        s.position.forEach(function(pos, i) {
          var x = i%cols, y = Math.floor(i/cols);
          options[pos] = options[pos]||{};
          options[pos] = extend({}, last, options[pos], {
            width: s.width,
            height: s.height,
            style: {
              left: '-'+s.width*x+'px',
              top: '-'+s.height*y+'px',
            }
          });
          last = options[key];
        });
      });
      return options;
    },
    androidHack = function(player) {
      // Android doesn't support :active and :hover on non-anchor and non-button elements
      // so, we need to fake the :active selector for thumbnails to show up.
      if (navigator.userAgent.toLowerCase().indexOf('android') !== -1) {
        var progressControl = player.controlBar.progressControl;
        var addFakeActive = function() {
          progressControl.addClass('fake-active');
        };
        var removeFakeActive = function() {
          progressControl.removeClass('fake-active');
        };
        progressControl.on('touchstart', addFakeActive);
        progressControl.on('touchend', removeFakeActive);
        progressControl.on('touchcancel', removeFakeActive);
      }
    },
    resolveUrl = function(baseUrl, url) {
      var base = document.createElement('base');
      base.href = baseUrl;
      var head = document.getElementsByTagName('head')[0];
      head.insertBefore(base, head.firstChild);
      var a = document.createElement('a');
      a.href = url;
      var result = a.href;
      head.removeChild(base);
      return result;
    },
    getConfByVtt = function(track, baseUrl) {
      var res = {}, cues = track.cues;
      for (var i = 0; i < cues.length; i++) {
        var cue = cues[i];
        var thumb = res[cue.startTime] = {
          src: resolveUrl(baseUrl, cue.text.split('#')[0]),
        };
        var opt = cue.text.split('#')[1];
        if (opt && opt.substring(0, 5) === 'xywh=') {
          var params = opt.substring(5).split(',');
          thumb.width = params[2];
          thumb.height = params[3];
          thumb.style = {
            left: '-'+params[0]+'px',
            top: '-'+params[1]+'px',
          };
        }
      }
      return res;
    },
    init = function(player, options) {
      if (player._thumbs) {
        player._thumbs.settings = options;
        return;
      }
      player._thumbs = {settings: options};
      androidHack(player);

      // create the thumbnail
      var div = document.createElement('div');
      div.className = 'vjs-thumbnail-holder';
      var img = document.createElement('img');
      div.appendChild(img);
      img.className = 'vjs-thumbnail';

      // keep track of the duration to calculate correct thumbnail to display
      var duration = player.duration();

      // when the container is MP4
      player.on('durationchange', function(event) {
        duration = player.duration();
      });

      // when the container is HLS
      player.on('loadedmetadata', function(event) {
        duration = player.duration();
      });

      // add the thumbnail to the player
      var progressControl = player.controlBar.progressControl;
      var seekBar = progressControl.seekBar;
      var el = progressControl.el();
      if (el.firstChild)
        el.insertBefore(div, el.firstChild);
      else
        el.appendChild(div);

      var moveListener = function(event) {
        if (event.target === div || event.target === img) {
          return;
        }
        var pageXOffset = getScrollOffset().x;
        var minLeft = seekBar.el().offsetLeft;
        var maxRight = minLeft+seekBar.el().offsetWidth;

        var pageX = event.pageX;
        if (event.changedTouches) {
          pageX = event.changedTouches[0].pageX;
        }
        // find the page offset of the mouse
        var left = pageX || (event.clientX + document.body.scrollLeft +
          document.documentElement.scrollLeft);
        // subtract the page offset of the positioned offset parent
        left -= offsetParent(progressControl.el()).getBoundingClientRect().left +
          pageXOffset;

        // apply updated styles to the thumbnail if necessary
        // mouseTime is the position of the mouse along the progress control bar
        // `left` applies to the mouse position relative to the player so we need
        // to remove the progress control's left offset to know the mouse position
        // relative to the progress control
        var mouseTime = Math.floor(seekBar.calculateDistance(event)*duration);
        var active = 0, settings = player._thumbs.settings;
        for (var time in settings) {
          if (mouseTime > time) {
            active = Math.max(active, time);
          }
        }
        var setting = settings[active];
        if (!setting) {
          return;
        }
        var re = /^https?:\/\//i;
        if (setting.src && img.src.replace(re, '//') !=
            setting.src.replace(re, '//')) {
          img.src = setting.src;
        }
        var scale = player.hasClass('vjs-fullscreen') ? 1.5 : 1;
        if (setting.style) {
          img.style.left = parseFloat(setting.style.left)*scale+'px';
          img.style.top = parseFloat(setting.style.top)*scale+'px';
        }
        if (img.naturalWidth) {
          img.style.width = img.naturalWidth*scale+'px';
          img.style.height = img.naturalHeight*scale+'px';
        }

        var width = parseFloat(setting.width || settings[0].width)*scale;
        var height = parseFloat(setting.height || settings[0].height)*scale;
        var halfWidth = width / 2;

        // make sure that the thumbnail doesn't fall off the right side of the
        // left side of the player
        if (left + halfWidth > maxRight) {
          left = maxRight - width;
        } else if (left-halfWidth < minLeft) {
          left = minLeft;
        } else {
          left -= halfWidth;
        }

        div.style.width = width + 'px';
        div.style.height = height + 'px';
        div.style.left = left + 'px';
        div.style.top = '-' + height + 'px';
        div.style.display = 'block';
      };

      // update the thumbnail while hovering
      progressControl.on('mousemove', moveListener);
      progressControl.on('touchmove', moveListener);

      var moveCancel = function(event) {
        div.style.display = 'none';
      };

      // move the placeholder out of the way when not hovering
      progressControl.on('mouseout', moveCancel);
      progressControl.on('touchcancel', moveCancel);
      progressControl.on('touchend', moveCancel);
      player.on('userinactive', moveCancel);
    };

  /**
   * register the thubmnails plugin
   */
  videojs.plugin('thumbnails', function(options) {
    if (!options) {
      return;
    }
    var player = this;
    if (!options.vtt) {
      init(player, unfoldSpritesConf(options));
      return;
    }
    var trackEl = player.addRemoteTextTrack({
      id: 'thumbnails',
      kind: 'metadata',
      src: options.vtt,
    });
    trackEl.addEventListener('load', function onLoad() {
      trackEl.removeEventListener('load', onLoad);
      var track = player.textTracks().getTrackById('thumbnails');
      init(player, getConfByVtt(track, options.vtt));
    });
  });
})();

},{}],169:[function(_dereq_,module,exports){
(function(window, vjs){
'use strict';
vjs.utils = vjs.utils||{};

function local_storage_init(){
    // Cookie functions from
    // https://developer.mozilla.org/en-US/docs/DOM/document.cookie
    function get_cookie_item(key){
        if (!key || !has_cookie_item(key))
            return null;
        var reg_ex = new RegExp('(?:^|.*;\\s*)'
        +window.escape(key).replace(/[\-\.\+\*]/g, '\\$&')
        +'\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*');
        return window.unescape(document.cookie.replace(reg_ex, '$1'));
    }
    function set_cookie_item(key, value, end, path, domain, secure){
        if (!key || /^(?:expires|max\-age|path|domain|secure)$/i.test(key))
            return;
        var expires = '';
        if (end)
        {
            switch (end.constructor)
            {
            case Number:
                expires = end===Infinity
                    ? '; expires=Tue, 19 Jan 2038 03:14:07 GMT'
                    : '; max-age=' + end;
                break;
            case String:
                expires = '; expires=' + end;
                break;
            case Date:
                expires = '; expires=' + end.toGMTString();
                break;
            }
        }
        document.cookie = window.escape(key)+'='+window.escape(value)+
            expires+(domain ? '; domain='+domain : '')+
            (path ? '; path=' + path : '')+
            (secure ? '; secure' : '');
    }
    function has_cookie_item(key){
        return (new RegExp('(?:^|;\\s*)'
            +window.escape(key).replace(/[\-\.\+\*]/g, '\\$&')+'\\s*\\=')
        ).test(document.cookie);
    }
    function has_local_storage(){
        try {
            window.localStorage.setItem('vjs-storage-test', 'value');
            window.localStorage.removeItem('vjs-storage-test');
            return true;
        } catch(e){ return false; }
    }
    vjs.utils.localStorage = has_local_storage() ? window.localStorage : {
        getItem: get_cookie_item,
        setItem: function(key, value){
            set_cookie_item(key, value, Infinity, '/');
        }
    };
}
local_storage_init();

})(window, window.videojs);

},{}],170:[function(_dereq_,module,exports){

},{}],171:[function(_dereq_,module,exports){
'use strict';
// For more information about browser field, check out the browser field at https://github.com/substack/browserify-handbook#browser-field.

var styleElementsInsertedAtTop = [];

var insertStyleElement = function(styleElement, options) {
    var head = document.head || document.getElementsByTagName('head')[0];
    var lastStyleElementInsertedAtTop = styleElementsInsertedAtTop[styleElementsInsertedAtTop.length - 1];

    options = options || {};
    options.insertAt = options.insertAt || 'bottom';

    if (options.insertAt === 'top') {
        if (!lastStyleElementInsertedAtTop) {
            head.insertBefore(styleElement, head.firstChild);
        } else if (lastStyleElementInsertedAtTop.nextSibling) {
            head.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
        } else {
            head.appendChild(styleElement);
        }
        styleElementsInsertedAtTop.push(styleElement);
    } else if (options.insertAt === 'bottom') {
        head.appendChild(styleElement);
    } else {
        throw new Error('Invalid value for parameter \'insertAt\'. Must be \'top\' or \'bottom\'.');
    }
};

module.exports = {
    // Create a <link> tag with optional data attributes
    createLink: function(href, attributes) {
        var head = document.head || document.getElementsByTagName('head')[0];
        var link = document.createElement('link');

        link.href = href;
        link.rel = 'stylesheet';

        for (var key in attributes) {
            if ( ! attributes.hasOwnProperty(key)) {
                continue;
            }
            var value = attributes[key];
            link.setAttribute('data-' + key, value);
        }

        head.appendChild(link);
    },
    // Create a <style> tag with optional data attributes
    createStyle: function(cssText, attributes, extraOptions) {
        extraOptions = extraOptions || {};

        var style = document.createElement('style');
        style.type = 'text/css';

        for (var key in attributes) {
            if ( ! attributes.hasOwnProperty(key)) {
                continue;
            }
            var value = attributes[key];
            style.setAttribute('data-' + key, value);
        }

        if (style.sheet) { // for jsdom and IE9+
            style.innerHTML = cssText;
            style.sheet.cssText = cssText;
            insertStyleElement(style, { insertAt: extraOptions.insertAt });
        } else if (style.styleSheet) { // for IE8 and below
            insertStyleElement(style, { insertAt: extraOptions.insertAt });
            style.styleSheet.cssText = cssText;
        } else { // for Chrome, Firefox, and Safari
            style.appendChild(document.createTextNode(cssText));
            insertStyleElement(style, { insertAt: extraOptions.insertAt });
        }
    }
};

},{}],172:[function(_dereq_,module,exports){
(function (global, factory) {
    if (typeof define === "function" && define.amd) {
        define(['module', 'select'], factory);
    } else if (typeof exports !== "undefined") {
        factory(module, _dereq_('select'));
    } else {
        var mod = {
            exports: {}
        };
        factory(mod, global.select);
        global.clipboardAction = mod.exports;
    }
})(this, function (module, _select) {
    'use strict';

    var _select2 = _interopRequireDefault(_select);

    function _interopRequireDefault(obj) {
        return obj && obj.__esModule ? obj : {
            default: obj
        };
    }

    var _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;
    };

    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) {
            throw new TypeError("Cannot call a class as a function");
        }
    }

    var _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;
        };
    }();

    var ClipboardAction = function () {
        /**
         * @param {Object} options
         */
        function ClipboardAction(options) {
            _classCallCheck(this, ClipboardAction);

            this.resolveOptions(options);
            this.initSelection();
        }

        /**
         * Defines base properties passed from constructor.
         * @param {Object} options
         */


        _createClass(ClipboardAction, [{
            key: 'resolveOptions',
            value: function resolveOptions() {
                var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

                this.action = options.action;
                this.container = options.container;
                this.emitter = options.emitter;
                this.target = options.target;
                this.text = options.text;
                this.trigger = options.trigger;

                this.selectedText = '';
            }
        }, {
            key: 'initSelection',
            value: function initSelection() {
                if (this.text) {
                    this.selectFake();
                } else if (this.target) {
                    this.selectTarget();
                }
            }
        }, {
            key: 'selectFake',
            value: function selectFake() {
                var _this = this;

                var isRTL = document.documentElement.getAttribute('dir') == 'rtl';

                this.removeFake();

                this.fakeHandlerCallback = function () {
                    return _this.removeFake();
                };
                this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;

                this.fakeElem = document.createElement('textarea');
                // Prevent zooming on iOS
                this.fakeElem.style.fontSize = '12pt';
                // Reset box model
                this.fakeElem.style.border = '0';
                this.fakeElem.style.padding = '0';
                this.fakeElem.style.margin = '0';
                // Move element out of screen horizontally
                this.fakeElem.style.position = 'absolute';
                this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px';
                // Move element to the same position vertically
                var yPosition = window.pageYOffset || document.documentElement.scrollTop;
                this.fakeElem.style.top = yPosition + 'px';

                this.fakeElem.setAttribute('readonly', '');
                this.fakeElem.value = this.text;

                this.container.appendChild(this.fakeElem);

                this.selectedText = (0, _select2.default)(this.fakeElem);
                this.copyText();
            }
        }, {
            key: 'removeFake',
            value: function removeFake() {
                if (this.fakeHandler) {
                    this.container.removeEventListener('click', this.fakeHandlerCallback);
                    this.fakeHandler = null;
                    this.fakeHandlerCallback = null;
                }

                if (this.fakeElem) {
                    this.container.removeChild(this.fakeElem);
                    this.fakeElem = null;
                }
            }
        }, {
            key: 'selectTarget',
            value: function selectTarget() {
                this.selectedText = (0, _select2.default)(this.target);
                this.copyText();
            }
        }, {
            key: 'copyText',
            value: function copyText() {
                var succeeded = void 0;

                try {
                    succeeded = document.execCommand(this.action);
                } catch (err) {
                    succeeded = false;
                }

                this.handleResult(succeeded);
            }
        }, {
            key: 'handleResult',
            value: function handleResult(succeeded) {
                this.emitter.emit(succeeded ? 'success' : 'error', {
                    action: this.action,
                    text: this.selectedText,
                    trigger: this.trigger,
                    clearSelection: this.clearSelection.bind(this)
                });
            }
        }, {
            key: 'clearSelection',
            value: function clearSelection() {
                if (this.trigger) {
                    this.trigger.focus();
                }

                window.getSelection().removeAllRanges();
            }
        }, {
            key: 'destroy',
            value: function destroy() {
                this.removeFake();
            }
        }, {
            key: 'action',
            set: function set() {
                var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy';

                this._action = action;

                if (this._action !== 'copy' && this._action !== 'cut') {
                    throw new Error('Invalid "action" value, use either "copy" or "cut"');
                }
            },
            get: function get() {
                return this._action;
            }
        }, {
            key: 'target',
            set: function set(target) {
                if (target !== undefined) {
                    if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && target.nodeType === 1) {
                        if (this.action === 'copy' && target.hasAttribute('disabled')) {
                            throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
                        }

                        if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {
                            throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');
                        }

                        this._target = target;
                    } else {
                        throw new Error('Invalid "target" value, use a valid Element');
                    }
                }
            },
            get: function get() {
                return this._target;
            }
        }]);

        return ClipboardAction;
    }();

    module.exports = ClipboardAction;
});
},{"select":386}],173:[function(_dereq_,module,exports){
(function (global, factory) {
    if (typeof define === "function" && define.amd) {
        define(['module', './clipboard-action', 'tiny-emitter', 'good-listener'], factory);
    } else if (typeof exports !== "undefined") {
        factory(module, _dereq_('./clipboard-action'), _dereq_('tiny-emitter'), _dereq_('good-listener'));
    } else {
        var mod = {
            exports: {}
        };
        factory(mod, global.clipboardAction, global.tinyEmitter, global.goodListener);
        global.clipboard = mod.exports;
    }
})(this, function (module, _clipboardAction, _tinyEmitter, _goodListener) {
    'use strict';

    var _clipboardAction2 = _interopRequireDefault(_clipboardAction);

    var _tinyEmitter2 = _interopRequireDefault(_tinyEmitter);

    var _goodListener2 = _interopRequireDefault(_goodListener);

    function _interopRequireDefault(obj) {
        return obj && obj.__esModule ? obj : {
            default: obj
        };
    }

    var _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;
    };

    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) {
            throw new TypeError("Cannot call a class as a function");
        }
    }

    var _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;
        };
    }();

    function _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;
    }

    function _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;
    }

    var Clipboard = function (_Emitter) {
        _inherits(Clipboard, _Emitter);

        /**
         * @param {String|HTMLElement|HTMLCollection|NodeList} trigger
         * @param {Object} options
         */
        function Clipboard(trigger, options) {
            _classCallCheck(this, Clipboard);

            var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this));

            _this.resolveOptions(options);
            _this.listenClick(trigger);
            return _this;
        }

        /**
         * Defines if attributes would be resolved using internal setter functions
         * or custom functions that were passed in the constructor.
         * @param {Object} options
         */


        _createClass(Clipboard, [{
            key: 'resolveOptions',
            value: function resolveOptions() {
                var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

                this.action = typeof options.action === 'function' ? options.action : this.defaultAction;
                this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;
                this.text = typeof options.text === 'function' ? options.text : this.defaultText;
                this.container = _typeof(options.container) === 'object' ? options.container : document.body;
            }
        }, {
            key: 'listenClick',
            value: function listenClick(trigger) {
                var _this2 = this;

                this.listener = (0, _goodListener2.default)(trigger, 'click', function (e) {
                    return _this2.onClick(e);
                });
            }
        }, {
            key: 'onClick',
            value: function onClick(e) {
                var trigger = e.delegateTarget || e.currentTarget;

                if (this.clipboardAction) {
                    this.clipboardAction = null;
                }

                this.clipboardAction = new _clipboardAction2.default({
                    action: this.action(trigger),
                    target: this.target(trigger),
                    text: this.text(trigger),
                    container: this.container,
                    trigger: trigger,
                    emitter: this
                });
            }
        }, {
            key: 'defaultAction',
            value: function defaultAction(trigger) {
                return getAttributeValue('action', trigger);
            }
        }, {
            key: 'defaultTarget',
            value: function defaultTarget(trigger) {
                var selector = getAttributeValue('target', trigger);

                if (selector) {
                    return document.querySelector(selector);
                }
            }
        }, {
            key: 'defaultText',
            value: function defaultText(trigger) {
                return getAttributeValue('text', trigger);
            }
        }, {
            key: 'destroy',
            value: function destroy() {
                this.listener.destroy();

                if (this.clipboardAction) {
                    this.clipboardAction.destroy();
                    this.clipboardAction = null;
                }
            }
        }], [{
            key: 'isSupported',
            value: function isSupported() {
                var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];

                var actions = typeof action === 'string' ? [action] : action;
                var support = !!document.queryCommandSupported;

                actions.forEach(function (action) {
                    support = support && !!document.queryCommandSupported(action);
                });

                return support;
            }
        }]);

        return Clipboard;
    }(_tinyEmitter2.default);

    /**
     * Helper function to retrieve attribute value.
     * @param {String} suffix
     * @param {Element} element
     */
    function getAttributeValue(suffix, element) {
        var attribute = 'data-clipboard-' + suffix;

        if (!element.hasAttribute(attribute)) {
            return;
        }

        return element.getAttribute(attribute);
    }

    module.exports = Clipboard;
});
},{"./clipboard-action":172,"good-listener":179,"tiny-emitter":387}],174:[function(_dereq_,module,exports){
var DOCUMENT_NODE_TYPE = 9;

/**
 * A polyfill for Element.matches()
 */
if (typeof Element !== 'undefined' && !Element.prototype.matches) {
    var proto = Element.prototype;

    proto.matches = proto.matchesSelector ||
                    proto.mozMatchesSelector ||
                    proto.msMatchesSelector ||
                    proto.oMatchesSelector ||
                    proto.webkitMatchesSelector;
}

/**
 * Finds the closest parent that matches a selector.
 *
 * @param {Element} element
 * @param {String} selector
 * @return {Function}
 */
function closest (element, selector) {
    while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {
        if (typeof element.matches === 'function' &&
            element.matches(selector)) {
          return element;
        }
        element = element.parentNode;
    }
}

module.exports = closest;

},{}],175:[function(_dereq_,module,exports){
var closest = _dereq_('./closest');

/**
 * Delegates event to a selector.
 *
 * @param {Element} element
 * @param {String} selector
 * @param {String} type
 * @param {Function} callback
 * @param {Boolean} useCapture
 * @return {Object}
 */
function _delegate(element, selector, type, callback, useCapture) {
    var listenerFn = listener.apply(this, arguments);

    element.addEventListener(type, listenerFn, useCapture);

    return {
        destroy: function() {
            element.removeEventListener(type, listenerFn, useCapture);
        }
    }
}

/**
 * Delegates event to a selector.
 *
 * @param {Element|String|Array} [elements]
 * @param {String} selector
 * @param {String} type
 * @param {Function} callback
 * @param {Boolean} useCapture
 * @return {Object}
 */
function delegate(elements, selector, type, callback, useCapture) {
    // Handle the regular Element usage
    if (typeof elements.addEventListener === 'function') {
        return _delegate.apply(null, arguments);
    }

    // Handle Element-less usage, it defaults to global delegation
    if (typeof type === 'function') {
        // Use `document` as the first parameter, then apply arguments
        // This is a short way to .unshift `arguments` without running into deoptimizations
        return _delegate.bind(null, document).apply(null, arguments);
    }

    // Handle Selector-based usage
    if (typeof elements === 'string') {
        elements = document.querySelectorAll(elements);
    }

    // Handle Array-like based usage
    return Array.prototype.map.call(elements, function (element) {
        return _delegate(element, selector, type, callback, useCapture);
    });
}

/**
 * Finds closest match and invokes callback.
 *
 * @param {Element} element
 * @param {String} selector
 * @param {String} type
 * @param {Function} callback
 * @return {Function}
 */
function listener(element, selector, type, callback) {
    return function(e) {
        e.delegateTarget = closest(e.target, selector);

        if (e.delegateTarget) {
            callback.call(element, e);
        }
    }
}

module.exports = delegate;

},{"./closest":174}],176:[function(_dereq_,module,exports){
(function (global){
var topLevel = typeof global !== 'undefined' ? global :
    typeof window !== 'undefined' ? window : {}
var minDoc = _dereq_('min-document');

var doccy;

if (typeof document !== 'undefined') {
    doccy = document;
} else {
    doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];

    if (!doccy) {
        doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
    }
}

module.exports = doccy;

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{"min-document":170}],177:[function(_dereq_,module,exports){
(function (global){
var win;

if (typeof window !== "undefined") {
    win = window;
} else if (typeof global !== "undefined") {
    win = global;
} else if (typeof self !== "undefined"){
    win = self;
} else {
    win = {};
}

module.exports = win;

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{}],178:[function(_dereq_,module,exports){
/**
 * Check if argument is a HTML element.
 *
 * @param {Object} value
 * @return {Boolean}
 */
exports.node = function(value) {
    return value !== undefined
        && value instanceof HTMLElement
        && value.nodeType === 1;
};

/**
 * Check if argument is a list of HTML elements.
 *
 * @param {Object} value
 * @return {Boolean}
 */
exports.nodeList = function(value) {
    var type = Object.prototype.toString.call(value);

    return value !== undefined
        && (type === '[object NodeList]' || type === '[object HTMLCollection]')
        && ('length' in value)
        && (value.length === 0 || exports.node(value[0]));
};

/**
 * Check if argument is a string.
 *
 * @param {Object} value
 * @return {Boolean}
 */
exports.string = function(value) {
    return typeof value === 'string'
        || value instanceof String;
};

/**
 * Check if argument is a function.
 *
 * @param {Object} value
 * @return {Boolean}
 */
exports.fn = function(value) {
    var type = Object.prototype.toString.call(value);

    return type === '[object Function]';
};

},{}],179:[function(_dereq_,module,exports){
var is = _dereq_('./is');
var delegate = _dereq_('delegate');

/**
 * Validates all params and calls the right
 * listener function based on its target type.
 *
 * @param {String|HTMLElement|HTMLCollection|NodeList} target
 * @param {String} type
 * @param {Function} callback
 * @return {Object}
 */
function listen(target, type, callback) {
    if (!target && !type && !callback) {
        throw new Error('Missing required arguments');
    }

    if (!is.string(type)) {
        throw new TypeError('Second argument must be a String');
    }

    if (!is.fn(callback)) {
        throw new TypeError('Third argument must be a Function');
    }

    if (is.node(target)) {
        return listenNode(target, type, callback);
    }
    else if (is.nodeList(target)) {
        return listenNodeList(target, type, callback);
    }
    else if (is.string(target)) {
        return listenSelector(target, type, callback);
    }
    else {
        throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');
    }
}

/**
 * Adds an event listener to a HTML element
 * and returns a remove listener function.
 *
 * @param {HTMLElement} node
 * @param {String} type
 * @param {Function} callback
 * @return {Object}
 */
function listenNode(node, type, callback) {
    node.addEventListener(type, callback);

    return {
        destroy: function() {
            node.removeEventListener(type, callback);
        }
    }
}

/**
 * Add an event listener to a list of HTML elements
 * and returns a remove listener function.
 *
 * @param {NodeList|HTMLCollection} nodeList
 * @param {String} type
 * @param {Function} callback
 * @return {Object}
 */
function listenNodeList(nodeList, type, callback) {
    Array.prototype.forEach.call(nodeList, function(node) {
        node.addEventListener(type, callback);
    });

    return {
        destroy: function() {
            Array.prototype.forEach.call(nodeList, function(node) {
                node.removeEventListener(type, callback);
            });
        }
    }
}

/**
 * Add an event listener to a selector
 * and returns a remove listener function.
 *
 * @param {String} selector
 * @param {String} type
 * @param {Function} callback
 * @return {Object}
 */
function listenSelector(selector, type, callback) {
    return delegate(document.body, selector, type, callback);
}

module.exports = listen;

},{"./is":178,"delegate":175}],180:[function(_dereq_,module,exports){
arguments[4][46][0].apply(exports,arguments)
},{"./_getNative":276,"./_root":321,"dup":46}],181:[function(_dereq_,module,exports){
arguments[4][47][0].apply(exports,arguments)
},{"./_hashClear":284,"./_hashDelete":285,"./_hashGet":286,"./_hashHas":287,"./_hashSet":288,"dup":47}],182:[function(_dereq_,module,exports){
arguments[4][48][0].apply(exports,arguments)
},{"./_listCacheClear":300,"./_listCacheDelete":301,"./_listCacheGet":302,"./_listCacheHas":303,"./_listCacheSet":304,"dup":48}],183:[function(_dereq_,module,exports){
arguments[4][49][0].apply(exports,arguments)
},{"./_getNative":276,"./_root":321,"dup":49}],184:[function(_dereq_,module,exports){
arguments[4][50][0].apply(exports,arguments)
},{"./_mapCacheClear":305,"./_mapCacheDelete":306,"./_mapCacheGet":307,"./_mapCacheHas":308,"./_mapCacheSet":309,"dup":50}],185:[function(_dereq_,module,exports){
arguments[4][51][0].apply(exports,arguments)
},{"./_getNative":276,"./_root":321,"dup":51}],186:[function(_dereq_,module,exports){
arguments[4][52][0].apply(exports,arguments)
},{"./_getNative":276,"./_root":321,"dup":52}],187:[function(_dereq_,module,exports){
arguments[4][53][0].apply(exports,arguments)
},{"./_MapCache":184,"./_setCacheAdd":323,"./_setCacheHas":324,"dup":53}],188:[function(_dereq_,module,exports){
arguments[4][54][0].apply(exports,arguments)
},{"./_ListCache":182,"./_stackClear":328,"./_stackDelete":329,"./_stackGet":330,"./_stackHas":331,"./_stackSet":332,"dup":54}],189:[function(_dereq_,module,exports){
arguments[4][55][0].apply(exports,arguments)
},{"./_root":321,"dup":55}],190:[function(_dereq_,module,exports){
arguments[4][56][0].apply(exports,arguments)
},{"./_root":321,"dup":56}],191:[function(_dereq_,module,exports){
arguments[4][57][0].apply(exports,arguments)
},{"./_getNative":276,"./_root":321,"dup":57}],192:[function(_dereq_,module,exports){
/**
 * A faster alternative to `Function#apply`, this function invokes `func`
 * with the `this` binding of `thisArg` and the arguments of `args`.
 *
 * @private
 * @param {Function} func The function to invoke.
 * @param {*} thisArg The `this` binding of `func`.
 * @param {Array} args The arguments to invoke `func` with.
 * @returns {*} Returns the result of `func`.
 */
function apply(func, thisArg, args) {
  switch (args.length) {
    case 0: return func.call(thisArg);
    case 1: return func.call(thisArg, args[0]);
    case 2: return func.call(thisArg, args[0], args[1]);
    case 3: return func.call(thisArg, args[0], args[1], args[2]);
  }
  return func.apply(thisArg, args);
}

module.exports = apply;

},{}],193:[function(_dereq_,module,exports){
/**
 * A specialized version of `_.forEach` for arrays without support for
 * iteratee shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns `array`.
 */
function arrayEach(array, iteratee) {
  var index = -1,
      length = array == null ? 0 : array.length;

  while (++index < length) {
    if (iteratee(array[index], index, array) === false) {
      break;
    }
  }
  return array;
}

module.exports = arrayEach;

},{}],194:[function(_dereq_,module,exports){
arguments[4][58][0].apply(exports,arguments)
},{"dup":58}],195:[function(_dereq_,module,exports){
arguments[4][59][0].apply(exports,arguments)
},{"./_baseTimes":243,"./_isIndex":293,"./isArguments":347,"./isArray":348,"./isBuffer":351,"./isTypedArray":361,"dup":59}],196:[function(_dereq_,module,exports){
arguments[4][60][0].apply(exports,arguments)
},{"dup":60}],197:[function(_dereq_,module,exports){
arguments[4][61][0].apply(exports,arguments)
},{"dup":61}],198:[function(_dereq_,module,exports){
arguments[4][62][0].apply(exports,arguments)
},{"dup":62}],199:[function(_dereq_,module,exports){
var baseAssignValue = _dereq_('./_baseAssignValue'),
    eq = _dereq_('./eq');

/**
 * This function is like `assignValue` except that it doesn't assign
 * `undefined` values.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {string} key The key of the property to assign.
 * @param {*} value The value to assign.
 */
function assignMergeValue(object, key, value) {
  if ((value !== undefined && !eq(object[key], value)) ||
      (value === undefined && !(key in object))) {
    baseAssignValue(object, key, value);
  }
}

module.exports = assignMergeValue;

},{"./_baseAssignValue":204,"./eq":339}],200:[function(_dereq_,module,exports){
var baseAssignValue = _dereq_('./_baseAssignValue'),
    eq = _dereq_('./eq');

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Assigns `value` to `key` of `object` if the existing value is not equivalent
 * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
 * for equality comparisons.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {string} key The key of the property to assign.
 * @param {*} value The value to assign.
 */
function assignValue(object, key, value) {
  var objValue = object[key];
  if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
      (value === undefined && !(key in object))) {
    baseAssignValue(object, key, value);
  }
}

module.exports = assignValue;

},{"./_baseAssignValue":204,"./eq":339}],201:[function(_dereq_,module,exports){
arguments[4][63][0].apply(exports,arguments)
},{"./eq":339,"dup":63}],202:[function(_dereq_,module,exports){
var copyObject = _dereq_('./_copyObject'),
    keys = _dereq_('./keys');

/**
 * The base implementation of `_.assign` without support for multiple sources
 * or `customizer` functions.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @returns {Object} Returns `object`.
 */
function baseAssign(object, source) {
  return object && copyObject(source, keys(source), object);
}

module.exports = baseAssign;

},{"./_copyObject":257,"./keys":362}],203:[function(_dereq_,module,exports){
var copyObject = _dereq_('./_copyObject'),
    keysIn = _dereq_('./keysIn');

/**
 * The base implementation of `_.assignIn` without support for multiple sources
 * or `customizer` functions.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @returns {Object} Returns `object`.
 */
function baseAssignIn(object, source) {
  return object && copyObject(source, keysIn(source), object);
}

module.exports = baseAssignIn;

},{"./_copyObject":257,"./keysIn":363}],204:[function(_dereq_,module,exports){
var defineProperty = _dereq_('./_defineProperty');

/**
 * The base implementation of `assignValue` and `assignMergeValue` without
 * value checks.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {string} key The key of the property to assign.
 * @param {*} value The value to assign.
 */
function baseAssignValue(object, key, value) {
  if (key == '__proto__' && defineProperty) {
    defineProperty(object, key, {
      'configurable': true,
      'enumerable': true,
      'value': value,
      'writable': true
    });
  } else {
    object[key] = value;
  }
}

module.exports = baseAssignValue;

},{"./_defineProperty":266}],205:[function(_dereq_,module,exports){
var Stack = _dereq_('./_Stack'),
    arrayEach = _dereq_('./_arrayEach'),
    assignValue = _dereq_('./_assignValue'),
    baseAssign = _dereq_('./_baseAssign'),
    baseAssignIn = _dereq_('./_baseAssignIn'),
    cloneBuffer = _dereq_('./_cloneBuffer'),
    copyArray = _dereq_('./_copyArray'),
    copySymbols = _dereq_('./_copySymbols'),
    copySymbolsIn = _dereq_('./_copySymbolsIn'),
    getAllKeys = _dereq_('./_getAllKeys'),
    getAllKeysIn = _dereq_('./_getAllKeysIn'),
    getTag = _dereq_('./_getTag'),
    initCloneArray = _dereq_('./_initCloneArray'),
    initCloneByTag = _dereq_('./_initCloneByTag'),
    initCloneObject = _dereq_('./_initCloneObject'),
    isArray = _dereq_('./isArray'),
    isBuffer = _dereq_('./isBuffer'),
    isMap = _dereq_('./isMap'),
    isObject = _dereq_('./isObject'),
    isSet = _dereq_('./isSet'),
    keys = _dereq_('./keys');

/** Used to compose bitmasks for cloning. */
var CLONE_DEEP_FLAG = 1,
    CLONE_FLAT_FLAG = 2,
    CLONE_SYMBOLS_FLAG = 4;

/** `Object#toString` result references. */
var argsTag = '[object Arguments]',
    arrayTag = '[object Array]',
    boolTag = '[object Boolean]',
    dateTag = '[object Date]',
    errorTag = '[object Error]',
    funcTag = '[object Function]',
    genTag = '[object GeneratorFunction]',
    mapTag = '[object Map]',
    numberTag = '[object Number]',
    objectTag = '[object Object]',
    regexpTag = '[object RegExp]',
    setTag = '[object Set]',
    stringTag = '[object String]',
    symbolTag = '[object Symbol]',
    weakMapTag = '[object WeakMap]';

var arrayBufferTag = '[object ArrayBuffer]',
    dataViewTag = '[object DataView]',
    float32Tag = '[object Float32Array]',
    float64Tag = '[object Float64Array]',
    int8Tag = '[object Int8Array]',
    int16Tag = '[object Int16Array]',
    int32Tag = '[object Int32Array]',
    uint8Tag = '[object Uint8Array]',
    uint8ClampedTag = '[object Uint8ClampedArray]',
    uint16Tag = '[object Uint16Array]',
    uint32Tag = '[object Uint32Array]';

/** Used to identify `toStringTag` values supported by `_.clone`. */
var cloneableTags = {};
cloneableTags[argsTag] = cloneableTags[arrayTag] =
cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
cloneableTags[boolTag] = cloneableTags[dateTag] =
cloneableTags[float32Tag] = cloneableTags[float64Tag] =
cloneableTags[int8Tag] = cloneableTags[int16Tag] =
cloneableTags[int32Tag] = cloneableTags[mapTag] =
cloneableTags[numberTag] = cloneableTags[objectTag] =
cloneableTags[regexpTag] = cloneableTags[setTag] =
cloneableTags[stringTag] = cloneableTags[symbolTag] =
cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
cloneableTags[errorTag] = cloneableTags[funcTag] =
cloneableTags[weakMapTag] = false;

/**
 * The base implementation of `_.clone` and `_.cloneDeep` which tracks
 * traversed objects.
 *
 * @private
 * @param {*} value The value to clone.
 * @param {boolean} bitmask The bitmask flags.
 *  1 - Deep clone
 *  2 - Flatten inherited properties
 *  4 - Clone symbols
 * @param {Function} [customizer] The function to customize cloning.
 * @param {string} [key] The key of `value`.
 * @param {Object} [object] The parent object of `value`.
 * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
 * @returns {*} Returns the cloned value.
 */
function baseClone(value, bitmask, customizer, key, object, stack) {
  var result,
      isDeep = bitmask & CLONE_DEEP_FLAG,
      isFlat = bitmask & CLONE_FLAT_FLAG,
      isFull = bitmask & CLONE_SYMBOLS_FLAG;

  if (customizer) {
    result = object ? customizer(value, key, object, stack) : customizer(value);
  }
  if (result !== undefined) {
    return result;
  }
  if (!isObject(value)) {
    return value;
  }
  var isArr = isArray(value);
  if (isArr) {
    result = initCloneArray(value);
    if (!isDeep) {
      return copyArray(value, result);
    }
  } else {
    var tag = getTag(value),
        isFunc = tag == funcTag || tag == genTag;

    if (isBuffer(value)) {
      return cloneBuffer(value, isDeep);
    }
    if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
      result = (isFlat || isFunc) ? {} : initCloneObject(value);
      if (!isDeep) {
        return isFlat
          ? copySymbolsIn(value, baseAssignIn(result, value))
          : copySymbols(value, baseAssign(result, value));
      }
    } else {
      if (!cloneableTags[tag]) {
        return object ? value : {};
      }
      result = initCloneByTag(value, tag, isDeep);
    }
  }
  // Check for circular references and return its corresponding clone.
  stack || (stack = new Stack);
  var stacked = stack.get(value);
  if (stacked) {
    return stacked;
  }
  stack.set(value, result);

  if (isSet(value)) {
    value.forEach(function(subValue) {
      result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
    });

    return result;
  }

  if (isMap(value)) {
    value.forEach(function(subValue, key) {
      result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
    });

    return result;
  }

  var keysFunc = isFull
    ? (isFlat ? getAllKeysIn : getAllKeys)
    : (isFlat ? keysIn : keys);

  var props = isArr ? undefined : keysFunc(value);
  arrayEach(props || value, function(subValue, key) {
    if (props) {
      key = subValue;
      subValue = value[key];
    }
    // Recursively populate clone (susceptible to call stack limits).
    assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
  });
  return result;
}

module.exports = baseClone;

},{"./_Stack":188,"./_arrayEach":193,"./_assignValue":200,"./_baseAssign":202,"./_baseAssignIn":203,"./_cloneBuffer":251,"./_copyArray":256,"./_copySymbols":258,"./_copySymbolsIn":259,"./_getAllKeys":272,"./_getAllKeysIn":273,"./_getTag":281,"./_initCloneArray":289,"./_initCloneByTag":290,"./_initCloneObject":291,"./isArray":348,"./isBuffer":351,"./isMap":354,"./isObject":355,"./isSet":358,"./keys":362}],206:[function(_dereq_,module,exports){
var isObject = _dereq_('./isObject');

/** Built-in value references. */
var objectCreate = Object.create;

/**
 * The base implementation of `_.create` without support for assigning
 * properties to the created object.
 *
 * @private
 * @param {Object} proto The object to inherit from.
 * @returns {Object} Returns the new object.
 */
var baseCreate = (function() {
  function object() {}
  return function(proto) {
    if (!isObject(proto)) {
      return {};
    }
    if (objectCreate) {
      return objectCreate(proto);
    }
    object.prototype = proto;
    var result = new object;
    object.prototype = undefined;
    return result;
  };
}());

module.exports = baseCreate;

},{"./isObject":355}],207:[function(_dereq_,module,exports){
var baseForOwn = _dereq_('./_baseForOwn'),
    createBaseEach = _dereq_('./_createBaseEach');

/**
 * The base implementation of `_.forEach` without support for iteratee shorthands.
 *
 * @private
 * @param {Array|Object} collection The collection to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array|Object} Returns `collection`.
 */
var baseEach = createBaseEach(baseForOwn);

module.exports = baseEach;

},{"./_baseForOwn":211,"./_createBaseEach":262}],208:[function(_dereq_,module,exports){
arguments[4][64][0].apply(exports,arguments)
},{"dup":64}],209:[function(_dereq_,module,exports){
var arrayPush = _dereq_('./_arrayPush'),
    isFlattenable = _dereq_('./_isFlattenable');

/**
 * The base implementation of `_.flatten` with support for restricting flattening.
 *
 * @private
 * @param {Array} array The array to flatten.
 * @param {number} depth The maximum recursion depth.
 * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
 * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
 * @param {Array} [result=[]] The initial result value.
 * @returns {Array} Returns the new flattened array.
 */
function baseFlatten(array, depth, predicate, isStrict, result) {
  var index = -1,
      length = array.length;

  predicate || (predicate = isFlattenable);
  result || (result = []);

  while (++index < length) {
    var value = array[index];
    if (depth > 0 && predicate(value)) {
      if (depth > 1) {
        // Recursively flatten arrays (susceptible to call stack limits).
        baseFlatten(value, depth - 1, predicate, isStrict, result);
      } else {
        arrayPush(result, value);
      }
    } else if (!isStrict) {
      result[result.length] = value;
    }
  }
  return result;
}

module.exports = baseFlatten;

},{"./_arrayPush":197,"./_isFlattenable":292}],210:[function(_dereq_,module,exports){
var createBaseFor = _dereq_('./_createBaseFor');

/**
 * The base implementation of `baseForOwn` which iterates over `object`
 * properties returned by `keysFunc` and invokes `iteratee` for each property.
 * Iteratee functions may exit iteration early by explicitly returning `false`.
 *
 * @private
 * @param {Object} object The object to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @param {Function} keysFunc The function to get the keys of `object`.
 * @returns {Object} Returns `object`.
 */
var baseFor = createBaseFor();

module.exports = baseFor;

},{"./_createBaseFor":263}],211:[function(_dereq_,module,exports){
var baseFor = _dereq_('./_baseFor'),
    keys = _dereq_('./keys');

/**
 * The base implementation of `_.forOwn` without support for iteratee shorthands.
 *
 * @private
 * @param {Object} object The object to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Object} Returns `object`.
 */
function baseForOwn(object, iteratee) {
  return object && baseFor(object, iteratee, keys);
}

module.exports = baseForOwn;

},{"./_baseFor":210,"./keys":362}],212:[function(_dereq_,module,exports){
arguments[4][65][0].apply(exports,arguments)
},{"./_castPath":249,"./_toKey":335,"dup":65}],213:[function(_dereq_,module,exports){
arguments[4][66][0].apply(exports,arguments)
},{"./_arrayPush":197,"./isArray":348,"dup":66}],214:[function(_dereq_,module,exports){
arguments[4][67][0].apply(exports,arguments)
},{"./_Symbol":189,"./_getRawTag":278,"./_objectToString":317,"dup":67}],215:[function(_dereq_,module,exports){
arguments[4][68][0].apply(exports,arguments)
},{"dup":68}],216:[function(_dereq_,module,exports){
var baseFindIndex = _dereq_('./_baseFindIndex'),
    baseIsNaN = _dereq_('./_baseIsNaN'),
    strictIndexOf = _dereq_('./_strictIndexOf');

/**
 * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} value The value to search for.
 * @param {number} fromIndex The index to search from.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function baseIndexOf(array, value, fromIndex) {
  return value === value
    ? strictIndexOf(array, value, fromIndex)
    : baseFindIndex(array, baseIsNaN, fromIndex);
}

module.exports = baseIndexOf;

},{"./_baseFindIndex":208,"./_baseIsNaN":222,"./_strictIndexOf":333}],217:[function(_dereq_,module,exports){
arguments[4][69][0].apply(exports,arguments)
},{"./_baseGetTag":214,"./isObjectLike":356,"dup":69}],218:[function(_dereq_,module,exports){
arguments[4][70][0].apply(exports,arguments)
},{"./_baseIsEqualDeep":219,"./isObjectLike":356,"dup":70}],219:[function(_dereq_,module,exports){
arguments[4][71][0].apply(exports,arguments)
},{"./_Stack":188,"./_equalArrays":267,"./_equalByTag":268,"./_equalObjects":269,"./_getTag":281,"./isArray":348,"./isBuffer":351,"./isTypedArray":361,"dup":71}],220:[function(_dereq_,module,exports){
var getTag = _dereq_('./_getTag'),
    isObjectLike = _dereq_('./isObjectLike');

/** `Object#toString` result references. */
var mapTag = '[object Map]';

/**
 * The base implementation of `_.isMap` without Node.js optimizations.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a map, else `false`.
 */
function baseIsMap(value) {
  return isObjectLike(value) && getTag(value) == mapTag;
}

module.exports = baseIsMap;

},{"./_getTag":281,"./isObjectLike":356}],221:[function(_dereq_,module,exports){
arguments[4][72][0].apply(exports,arguments)
},{"./_Stack":188,"./_baseIsEqual":218,"dup":72}],222:[function(_dereq_,module,exports){
/**
 * The base implementation of `_.isNaN` without support for number objects.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
 */
function baseIsNaN(value) {
  return value !== value;
}

module.exports = baseIsNaN;

},{}],223:[function(_dereq_,module,exports){
arguments[4][73][0].apply(exports,arguments)
},{"./_isMasked":297,"./_toSource":336,"./isFunction":352,"./isObject":355,"dup":73}],224:[function(_dereq_,module,exports){
var getTag = _dereq_('./_getTag'),
    isObjectLike = _dereq_('./isObjectLike');

/** `Object#toString` result references. */
var setTag = '[object Set]';

/**
 * The base implementation of `_.isSet` without Node.js optimizations.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a set, else `false`.
 */
function baseIsSet(value) {
  return isObjectLike(value) && getTag(value) == setTag;
}

module.exports = baseIsSet;

},{"./_getTag":281,"./isObjectLike":356}],225:[function(_dereq_,module,exports){
arguments[4][74][0].apply(exports,arguments)
},{"./_baseGetTag":214,"./isLength":353,"./isObjectLike":356,"dup":74}],226:[function(_dereq_,module,exports){
arguments[4][75][0].apply(exports,arguments)
},{"./_baseMatches":230,"./_baseMatchesProperty":231,"./identity":345,"./isArray":348,"./property":371,"dup":75}],227:[function(_dereq_,module,exports){
arguments[4][76][0].apply(exports,arguments)
},{"./_isPrototype":298,"./_nativeKeys":314,"dup":76}],228:[function(_dereq_,module,exports){
var isObject = _dereq_('./isObject'),
    isPrototype = _dereq_('./_isPrototype'),
    nativeKeysIn = _dereq_('./_nativeKeysIn');

/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 */
function baseKeysIn(object) {
  if (!isObject(object)) {
    return nativeKeysIn(object);
  }
  var isProto = isPrototype(object),
      result = [];

  for (var key in object) {
    if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
      result.push(key);
    }
  }
  return result;
}

module.exports = baseKeysIn;

},{"./_isPrototype":298,"./_nativeKeysIn":315,"./isObject":355}],229:[function(_dereq_,module,exports){
var baseEach = _dereq_('./_baseEach'),
    isArrayLike = _dereq_('./isArrayLike');

/**
 * The base implementation of `_.map` without support for iteratee shorthands.
 *
 * @private
 * @param {Array|Object} collection The collection to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns the new mapped array.
 */
function baseMap(collection, iteratee) {
  var index = -1,
      result = isArrayLike(collection) ? Array(collection.length) : [];

  baseEach(collection, function(value, key, collection) {
    result[++index] = iteratee(value, key, collection);
  });
  return result;
}

module.exports = baseMap;

},{"./_baseEach":207,"./isArrayLike":349}],230:[function(_dereq_,module,exports){
arguments[4][77][0].apply(exports,arguments)
},{"./_baseIsMatch":221,"./_getMatchData":275,"./_matchesStrictComparable":311,"dup":77}],231:[function(_dereq_,module,exports){
arguments[4][78][0].apply(exports,arguments)
},{"./_baseIsEqual":218,"./_isKey":295,"./_isStrictComparable":299,"./_matchesStrictComparable":311,"./_toKey":335,"./get":343,"./hasIn":344,"dup":78}],232:[function(_dereq_,module,exports){
var Stack = _dereq_('./_Stack'),
    assignMergeValue = _dereq_('./_assignMergeValue'),
    baseFor = _dereq_('./_baseFor'),
    baseMergeDeep = _dereq_('./_baseMergeDeep'),
    isObject = _dereq_('./isObject'),
    keysIn = _dereq_('./keysIn'),
    safeGet = _dereq_('./_safeGet');

/**
 * The base implementation of `_.merge` without support for multiple sources.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @param {number} srcIndex The index of `source`.
 * @param {Function} [customizer] The function to customize merged values.
 * @param {Object} [stack] Tracks traversed source values and their merged
 *  counterparts.
 */
function baseMerge(object, source, srcIndex, customizer, stack) {
  if (object === source) {
    return;
  }
  baseFor(source, function(srcValue, key) {
    if (isObject(srcValue)) {
      stack || (stack = new Stack);
      baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
    }
    else {
      var newValue = customizer
        ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack)
        : undefined;

      if (newValue === undefined) {
        newValue = srcValue;
      }
      assignMergeValue(object, key, newValue);
    }
  }, keysIn);
}

module.exports = baseMerge;

},{"./_Stack":188,"./_assignMergeValue":199,"./_baseFor":210,"./_baseMergeDeep":233,"./_safeGet":322,"./isObject":355,"./keysIn":363}],233:[function(_dereq_,module,exports){
var assignMergeValue = _dereq_('./_assignMergeValue'),
    cloneBuffer = _dereq_('./_cloneBuffer'),
    cloneTypedArray = _dereq_('./_cloneTypedArray'),
    copyArray = _dereq_('./_copyArray'),
    initCloneObject = _dereq_('./_initCloneObject'),
    isArguments = _dereq_('./isArguments'),
    isArray = _dereq_('./isArray'),
    isArrayLikeObject = _dereq_('./isArrayLikeObject'),
    isBuffer = _dereq_('./isBuffer'),
    isFunction = _dereq_('./isFunction'),
    isObject = _dereq_('./isObject'),
    isPlainObject = _dereq_('./isPlainObject'),
    isTypedArray = _dereq_('./isTypedArray'),
    safeGet = _dereq_('./_safeGet'),
    toPlainObject = _dereq_('./toPlainObject');

/**
 * A specialized version of `baseMerge` for arrays and objects which performs
 * deep merges and tracks traversed objects enabling objects with circular
 * references to be merged.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @param {string} key The key of the value to merge.
 * @param {number} srcIndex The index of `source`.
 * @param {Function} mergeFunc The function to merge values.
 * @param {Function} [customizer] The function to customize assigned values.
 * @param {Object} [stack] Tracks traversed source values and their merged
 *  counterparts.
 */
function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {
  var objValue = safeGet(object, key),
      srcValue = safeGet(source, key),
      stacked = stack.get(srcValue);

  if (stacked) {
    assignMergeValue(object, key, stacked);
    return;
  }
  var newValue = customizer
    ? customizer(objValue, srcValue, (key + ''), object, source, stack)
    : undefined;

  var isCommon = newValue === undefined;

  if (isCommon) {
    var isArr = isArray(srcValue),
        isBuff = !isArr && isBuffer(srcValue),
        isTyped = !isArr && !isBuff && isTypedArray(srcValue);

    newValue = srcValue;
    if (isArr || isBuff || isTyped) {
      if (isArray(objValue)) {
        newValue = objValue;
      }
      else if (isArrayLikeObject(objValue)) {
        newValue = copyArray(objValue);
      }
      else if (isBuff) {
        isCommon = false;
        newValue = cloneBuffer(srcValue, true);
      }
      else if (isTyped) {
        isCommon = false;
        newValue = cloneTypedArray(srcValue, true);
      }
      else {
        newValue = [];
      }
    }
    else if (isPlainObject(srcValue) || isArguments(srcValue)) {
      newValue = objValue;
      if (isArguments(objValue)) {
        newValue = toPlainObject(objValue);
      }
      else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) {
        newValue = initCloneObject(srcValue);
      }
    }
    else {
      isCommon = false;
    }
  }
  if (isCommon) {
    // Recursively merge objects and arrays (susceptible to call stack limits).
    stack.set(srcValue, newValue);
    mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
    stack['delete'](srcValue);
  }
  assignMergeValue(object, key, newValue);
}

module.exports = baseMergeDeep;

},{"./_assignMergeValue":199,"./_cloneBuffer":251,"./_cloneTypedArray":255,"./_copyArray":256,"./_initCloneObject":291,"./_safeGet":322,"./isArguments":347,"./isArray":348,"./isArrayLikeObject":350,"./isBuffer":351,"./isFunction":352,"./isObject":355,"./isPlainObject":357,"./isTypedArray":361,"./toPlainObject":379}],234:[function(_dereq_,module,exports){
var basePickBy = _dereq_('./_basePickBy'),
    hasIn = _dereq_('./hasIn');

/**
 * The base implementation of `_.pick` without support for individual
 * property identifiers.
 *
 * @private
 * @param {Object} object The source object.
 * @param {string[]} paths The property paths to pick.
 * @returns {Object} Returns the new object.
 */
function basePick(object, paths) {
  return basePickBy(object, paths, function(value, path) {
    return hasIn(object, path);
  });
}

module.exports = basePick;

},{"./_basePickBy":235,"./hasIn":344}],235:[function(_dereq_,module,exports){
var baseGet = _dereq_('./_baseGet'),
    baseSet = _dereq_('./_baseSet'),
    castPath = _dereq_('./_castPath');

/**
 * The base implementation of  `_.pickBy` without support for iteratee shorthands.
 *
 * @private
 * @param {Object} object The source object.
 * @param {string[]} paths The property paths to pick.
 * @param {Function} predicate The function invoked per property.
 * @returns {Object} Returns the new object.
 */
function basePickBy(object, paths, predicate) {
  var index = -1,
      length = paths.length,
      result = {};

  while (++index < length) {
    var path = paths[index],
        value = baseGet(object, path);

    if (predicate(value, path)) {
      baseSet(result, castPath(path, object), value);
    }
  }
  return result;
}

module.exports = basePickBy;

},{"./_baseGet":212,"./_baseSet":239,"./_castPath":249}],236:[function(_dereq_,module,exports){
arguments[4][79][0].apply(exports,arguments)
},{"dup":79}],237:[function(_dereq_,module,exports){
arguments[4][80][0].apply(exports,arguments)
},{"./_baseGet":212,"dup":80}],238:[function(_dereq_,module,exports){
var identity = _dereq_('./identity'),
    overRest = _dereq_('./_overRest'),
    setToString = _dereq_('./_setToString');

/**
 * The base implementation of `_.rest` which doesn't validate or coerce arguments.
 *
 * @private
 * @param {Function} func The function to apply a rest parameter to.
 * @param {number} [start=func.length-1] The start position of the rest parameter.
 * @returns {Function} Returns the new function.
 */
function baseRest(func, start) {
  return setToString(overRest(func, start, identity), func + '');
}

module.exports = baseRest;

},{"./_overRest":319,"./_setToString":326,"./identity":345}],239:[function(_dereq_,module,exports){
var assignValue = _dereq_('./_assignValue'),
    castPath = _dereq_('./_castPath'),
    isIndex = _dereq_('./_isIndex'),
    isObject = _dereq_('./isObject'),
    toKey = _dereq_('./_toKey');

/**
 * The base implementation of `_.set`.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {Array|string} path The path of the property to set.
 * @param {*} value The value to set.
 * @param {Function} [customizer] The function to customize path creation.
 * @returns {Object} Returns `object`.
 */
function baseSet(object, path, value, customizer) {
  if (!isObject(object)) {
    return object;
  }
  path = castPath(path, object);

  var index = -1,
      length = path.length,
      lastIndex = length - 1,
      nested = object;

  while (nested != null && ++index < length) {
    var key = toKey(path[index]),
        newValue = value;

    if (index != lastIndex) {
      var objValue = nested[key];
      newValue = customizer ? customizer(objValue, key, nested) : undefined;
      if (newValue === undefined) {
        newValue = isObject(objValue)
          ? objValue
          : (isIndex(path[index + 1]) ? [] : {});
      }
    }
    assignValue(nested, key, newValue);
    nested = nested[key];
  }
  return object;
}

module.exports = baseSet;

},{"./_assignValue":200,"./_castPath":249,"./_isIndex":293,"./_toKey":335,"./isObject":355}],240:[function(_dereq_,module,exports){
var constant = _dereq_('./constant'),
    defineProperty = _dereq_('./_defineProperty'),
    identity = _dereq_('./identity');

/**
 * The base implementation of `setToString` without support for hot loop shorting.
 *
 * @private
 * @param {Function} func The function to modify.
 * @param {Function} string The `toString` result.
 * @returns {Function} Returns `func`.
 */
var baseSetToString = !defineProperty ? identity : function(func, string) {
  return defineProperty(func, 'toString', {
    'configurable': true,
    'enumerable': false,
    'value': constant(string),
    'writable': true
  });
};

module.exports = baseSetToString;

},{"./_defineProperty":266,"./constant":337,"./identity":345}],241:[function(_dereq_,module,exports){
/**
 * The base implementation of `_.slice` without an iteratee call guard.
 *
 * @private
 * @param {Array} array The array to slice.
 * @param {number} [start=0] The start position.
 * @param {number} [end=array.length] The end position.
 * @returns {Array} Returns the slice of `array`.
 */
function baseSlice(array, start, end) {
  var index = -1,
      length = array.length;

  if (start < 0) {
    start = -start > length ? 0 : (length + start);
  }
  end = end > length ? length : end;
  if (end < 0) {
    end += length;
  }
  length = start > end ? 0 : ((end - start) >>> 0);
  start >>>= 0;

  var result = Array(length);
  while (++index < length) {
    result[index] = array[index + start];
  }
  return result;
}

module.exports = baseSlice;

},{}],242:[function(_dereq_,module,exports){
var baseEach = _dereq_('./_baseEach');

/**
 * The base implementation of `_.some` without support for iteratee shorthands.
 *
 * @private
 * @param {Array|Object} collection The collection to iterate over.
 * @param {Function} predicate The function invoked per iteration.
 * @returns {boolean} Returns `true` if any element passes the predicate check,
 *  else `false`.
 */
function baseSome(collection, predicate) {
  var result;

  baseEach(collection, function(value, index, collection) {
    result = predicate(value, index, collection);
    return !result;
  });
  return !!result;
}

module.exports = baseSome;

},{"./_baseEach":207}],243:[function(_dereq_,module,exports){
arguments[4][81][0].apply(exports,arguments)
},{"dup":81}],244:[function(_dereq_,module,exports){
arguments[4][82][0].apply(exports,arguments)
},{"./_Symbol":189,"./_arrayMap":196,"./isArray":348,"./isSymbol":360,"dup":82}],245:[function(_dereq_,module,exports){
arguments[4][83][0].apply(exports,arguments)
},{"dup":83}],246:[function(_dereq_,module,exports){
var castPath = _dereq_('./_castPath'),
    last = _dereq_('./last'),
    parent = _dereq_('./_parent'),
    toKey = _dereq_('./_toKey');

/**
 * The base implementation of `_.unset`.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {Array|string} path The property path to unset.
 * @returns {boolean} Returns `true` if the property is deleted, else `false`.
 */
function baseUnset(object, path) {
  path = castPath(path, object);
  object = parent(object, path);
  return object == null || delete object[toKey(last(path))];
}

module.exports = baseUnset;

},{"./_castPath":249,"./_parent":320,"./_toKey":335,"./last":364}],247:[function(_dereq_,module,exports){
var arrayMap = _dereq_('./_arrayMap');

/**
 * The base implementation of `_.values` and `_.valuesIn` which creates an
 * array of `object` property values corresponding to the property names
 * of `props`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {Array} props The property names to get values for.
 * @returns {Object} Returns the array of property values.
 */
function baseValues(object, props) {
  return arrayMap(props, function(key) {
    return object[key];
  });
}

module.exports = baseValues;

},{"./_arrayMap":196}],248:[function(_dereq_,module,exports){
arguments[4][84][0].apply(exports,arguments)
},{"dup":84}],249:[function(_dereq_,module,exports){
arguments[4][85][0].apply(exports,arguments)
},{"./_isKey":295,"./_stringToPath":334,"./isArray":348,"./toString":380,"dup":85}],250:[function(_dereq_,module,exports){
var Uint8Array = _dereq_('./_Uint8Array');

/**
 * Creates a clone of `arrayBuffer`.
 *
 * @private
 * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
 * @returns {ArrayBuffer} Returns the cloned array buffer.
 */
function cloneArrayBuffer(arrayBuffer) {
  var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
  new Uint8Array(result).set(new Uint8Array(arrayBuffer));
  return result;
}

module.exports = cloneArrayBuffer;

},{"./_Uint8Array":190}],251:[function(_dereq_,module,exports){
var root = _dereq_('./_root');

/** Detect free variable `exports`. */
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;

/** Detect free variable `module`. */
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;

/** Detect the popular CommonJS extension `module.exports`. */
var moduleExports = freeModule && freeModule.exports === freeExports;

/** Built-in value references. */
var Buffer = moduleExports ? root.Buffer : undefined,
    allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined;

/**
 * Creates a clone of  `buffer`.
 *
 * @private
 * @param {Buffer} buffer The buffer to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Buffer} Returns the cloned buffer.
 */
function cloneBuffer(buffer, isDeep) {
  if (isDeep) {
    return buffer.slice();
  }
  var length = buffer.length,
      result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);

  buffer.copy(result);
  return result;
}

module.exports = cloneBuffer;

},{"./_root":321}],252:[function(_dereq_,module,exports){
var cloneArrayBuffer = _dereq_('./_cloneArrayBuffer');

/**
 * Creates a clone of `dataView`.
 *
 * @private
 * @param {Object} dataView The data view to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the cloned data view.
 */
function cloneDataView(dataView, isDeep) {
  var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
  return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
}

module.exports = cloneDataView;

},{"./_cloneArrayBuffer":250}],253:[function(_dereq_,module,exports){
/** Used to match `RegExp` flags from their coerced string values. */
var reFlags = /\w*$/;

/**
 * Creates a clone of `regexp`.
 *
 * @private
 * @param {Object} regexp The regexp to clone.
 * @returns {Object} Returns the cloned regexp.
 */
function cloneRegExp(regexp) {
  var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
  result.lastIndex = regexp.lastIndex;
  return result;
}

module.exports = cloneRegExp;

},{}],254:[function(_dereq_,module,exports){
var Symbol = _dereq_('./_Symbol');

/** Used to convert symbols to primitives and strings. */
var symbolProto = Symbol ? Symbol.prototype : undefined,
    symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;

/**
 * Creates a clone of the `symbol` object.
 *
 * @private
 * @param {Object} symbol The symbol object to clone.
 * @returns {Object} Returns the cloned symbol object.
 */
function cloneSymbol(symbol) {
  return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
}

module.exports = cloneSymbol;

},{"./_Symbol":189}],255:[function(_dereq_,module,exports){
var cloneArrayBuffer = _dereq_('./_cloneArrayBuffer');

/**
 * Creates a clone of `typedArray`.
 *
 * @private
 * @param {Object} typedArray The typed array to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the cloned typed array.
 */
function cloneTypedArray(typedArray, isDeep) {
  var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
  return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
}

module.exports = cloneTypedArray;

},{"./_cloneArrayBuffer":250}],256:[function(_dereq_,module,exports){
/**
 * Copies the values of `source` to `array`.
 *
 * @private
 * @param {Array} source The array to copy values from.
 * @param {Array} [array=[]] The array to copy values to.
 * @returns {Array} Returns `array`.
 */
function copyArray(source, array) {
  var index = -1,
      length = source.length;

  array || (array = Array(length));
  while (++index < length) {
    array[index] = source[index];
  }
  return array;
}

module.exports = copyArray;

},{}],257:[function(_dereq_,module,exports){
var assignValue = _dereq_('./_assignValue'),
    baseAssignValue = _dereq_('./_baseAssignValue');

/**
 * Copies properties of `source` to `object`.
 *
 * @private
 * @param {Object} source The object to copy properties from.
 * @param {Array} props The property identifiers to copy.
 * @param {Object} [object={}] The object to copy properties to.
 * @param {Function} [customizer] The function to customize copied values.
 * @returns {Object} Returns `object`.
 */
function copyObject(source, props, object, customizer) {
  var isNew = !object;
  object || (object = {});

  var index = -1,
      length = props.length;

  while (++index < length) {
    var key = props[index];

    var newValue = customizer
      ? customizer(object[key], source[key], key, object, source)
      : undefined;

    if (newValue === undefined) {
      newValue = source[key];
    }
    if (isNew) {
      baseAssignValue(object, key, newValue);
    } else {
      assignValue(object, key, newValue);
    }
  }
  return object;
}

module.exports = copyObject;

},{"./_assignValue":200,"./_baseAssignValue":204}],258:[function(_dereq_,module,exports){
var copyObject = _dereq_('./_copyObject'),
    getSymbols = _dereq_('./_getSymbols');

/**
 * Copies own symbols of `source` to `object`.
 *
 * @private
 * @param {Object} source The object to copy symbols from.
 * @param {Object} [object={}] The object to copy symbols to.
 * @returns {Object} Returns `object`.
 */
function copySymbols(source, object) {
  return copyObject(source, getSymbols(source), object);
}

module.exports = copySymbols;

},{"./_copyObject":257,"./_getSymbols":279}],259:[function(_dereq_,module,exports){
var copyObject = _dereq_('./_copyObject'),
    getSymbolsIn = _dereq_('./_getSymbolsIn');

/**
 * Copies own and inherited symbols of `source` to `object`.
 *
 * @private
 * @param {Object} source The object to copy symbols from.
 * @param {Object} [object={}] The object to copy symbols to.
 * @returns {Object} Returns `object`.
 */
function copySymbolsIn(source, object) {
  return copyObject(source, getSymbolsIn(source), object);
}

module.exports = copySymbolsIn;

},{"./_copyObject":257,"./_getSymbolsIn":280}],260:[function(_dereq_,module,exports){
arguments[4][86][0].apply(exports,arguments)
},{"./_root":321,"dup":86}],261:[function(_dereq_,module,exports){
var baseRest = _dereq_('./_baseRest'),
    isIterateeCall = _dereq_('./_isIterateeCall');

/**
 * Creates a function like `_.assign`.
 *
 * @private
 * @param {Function} assigner The function to assign values.
 * @returns {Function} Returns the new assigner function.
 */
function createAssigner(assigner) {
  return baseRest(function(object, sources) {
    var index = -1,
        length = sources.length,
        customizer = length > 1 ? sources[length - 1] : undefined,
        guard = length > 2 ? sources[2] : undefined;

    customizer = (assigner.length > 3 && typeof customizer == 'function')
      ? (length--, customizer)
      : undefined;

    if (guard && isIterateeCall(sources[0], sources[1], guard)) {
      customizer = length < 3 ? undefined : customizer;
      length = 1;
    }
    object = Object(object);
    while (++index < length) {
      var source = sources[index];
      if (source) {
        assigner(object, source, index, customizer);
      }
    }
    return object;
  });
}

module.exports = createAssigner;

},{"./_baseRest":238,"./_isIterateeCall":294}],262:[function(_dereq_,module,exports){
var isArrayLike = _dereq_('./isArrayLike');

/**
 * Creates a `baseEach` or `baseEachRight` function.
 *
 * @private
 * @param {Function} eachFunc The function to iterate over a collection.
 * @param {boolean} [fromRight] Specify iterating from right to left.
 * @returns {Function} Returns the new base function.
 */
function createBaseEach(eachFunc, fromRight) {
  return function(collection, iteratee) {
    if (collection == null) {
      return collection;
    }
    if (!isArrayLike(collection)) {
      return eachFunc(collection, iteratee);
    }
    var length = collection.length,
        index = fromRight ? length : -1,
        iterable = Object(collection);

    while ((fromRight ? index-- : ++index < length)) {
      if (iteratee(iterable[index], index, iterable) === false) {
        break;
      }
    }
    return collection;
  };
}

module.exports = createBaseEach;

},{"./isArrayLike":349}],263:[function(_dereq_,module,exports){
/**
 * Creates a base function for methods like `_.forIn` and `_.forOwn`.
 *
 * @private
 * @param {boolean} [fromRight] Specify iterating from right to left.
 * @returns {Function} Returns the new base function.
 */
function createBaseFor(fromRight) {
  return function(object, iteratee, keysFunc) {
    var index = -1,
        iterable = Object(object),
        props = keysFunc(object),
        length = props.length;

    while (length--) {
      var key = props[fromRight ? length : ++index];
      if (iteratee(iterable[key], key, iterable) === false) {
        break;
      }
    }
    return object;
  };
}

module.exports = createBaseFor;

},{}],264:[function(_dereq_,module,exports){
arguments[4][87][0].apply(exports,arguments)
},{"./_baseIteratee":226,"./isArrayLike":349,"./keys":362,"dup":87}],265:[function(_dereq_,module,exports){
var isPlainObject = _dereq_('./isPlainObject');

/**
 * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain
 * objects.
 *
 * @private
 * @param {*} value The value to inspect.
 * @param {string} key The key of the property to inspect.
 * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`.
 */
function customOmitClone(value) {
  return isPlainObject(value) ? undefined : value;
}

module.exports = customOmitClone;

},{"./isPlainObject":357}],266:[function(_dereq_,module,exports){
var getNative = _dereq_('./_getNative');

var defineProperty = (function() {
  try {
    var func = getNative(Object, 'defineProperty');
    func({}, '', {});
    return func;
  } catch (e) {}
}());

module.exports = defineProperty;

},{"./_getNative":276}],267:[function(_dereq_,module,exports){
arguments[4][88][0].apply(exports,arguments)
},{"./_SetCache":187,"./_arraySome":198,"./_cacheHas":248,"dup":88}],268:[function(_dereq_,module,exports){
arguments[4][89][0].apply(exports,arguments)
},{"./_Symbol":189,"./_Uint8Array":190,"./_equalArrays":267,"./_mapToArray":310,"./_setToArray":325,"./eq":339,"dup":89}],269:[function(_dereq_,module,exports){
arguments[4][90][0].apply(exports,arguments)
},{"./_getAllKeys":272,"dup":90}],270:[function(_dereq_,module,exports){
var flatten = _dereq_('./flatten'),
    overRest = _dereq_('./_overRest'),
    setToString = _dereq_('./_setToString');

/**
 * A specialized version of `baseRest` which flattens the rest array.
 *
 * @private
 * @param {Function} func The function to apply a rest parameter to.
 * @returns {Function} Returns the new function.
 */
function flatRest(func) {
  return setToString(overRest(func, undefined, flatten), func + '');
}

module.exports = flatRest;

},{"./_overRest":319,"./_setToString":326,"./flatten":342}],271:[function(_dereq_,module,exports){
(function (global){
/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;

module.exports = freeGlobal;

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{}],272:[function(_dereq_,module,exports){
arguments[4][92][0].apply(exports,arguments)
},{"./_baseGetAllKeys":213,"./_getSymbols":279,"./keys":362,"dup":92}],273:[function(_dereq_,module,exports){
var baseGetAllKeys = _dereq_('./_baseGetAllKeys'),
    getSymbolsIn = _dereq_('./_getSymbolsIn'),
    keysIn = _dereq_('./keysIn');

/**
 * Creates an array of own and inherited enumerable property names and
 * symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names and symbols.
 */
function getAllKeysIn(object) {
  return baseGetAllKeys(object, keysIn, getSymbolsIn);
}

module.exports = getAllKeysIn;

},{"./_baseGetAllKeys":213,"./_getSymbolsIn":280,"./keysIn":363}],274:[function(_dereq_,module,exports){
arguments[4][93][0].apply(exports,arguments)
},{"./_isKeyable":296,"dup":93}],275:[function(_dereq_,module,exports){
arguments[4][94][0].apply(exports,arguments)
},{"./_isStrictComparable":299,"./keys":362,"dup":94}],276:[function(_dereq_,module,exports){
arguments[4][95][0].apply(exports,arguments)
},{"./_baseIsNative":223,"./_getValue":282,"dup":95}],277:[function(_dereq_,module,exports){
var overArg = _dereq_('./_overArg');

/** Built-in value references. */
var getPrototype = overArg(Object.getPrototypeOf, Object);

module.exports = getPrototype;

},{"./_overArg":318}],278:[function(_dereq_,module,exports){
arguments[4][96][0].apply(exports,arguments)
},{"./_Symbol":189,"dup":96}],279:[function(_dereq_,module,exports){
arguments[4][97][0].apply(exports,arguments)
},{"./_arrayFilter":194,"./stubArray":373,"dup":97}],280:[function(_dereq_,module,exports){
var arrayPush = _dereq_('./_arrayPush'),
    getPrototype = _dereq_('./_getPrototype'),
    getSymbols = _dereq_('./_getSymbols'),
    stubArray = _dereq_('./stubArray');

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeGetSymbols = Object.getOwnPropertySymbols;

/**
 * Creates an array of the own and inherited enumerable symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of symbols.
 */
var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) {
  var result = [];
  while (object) {
    arrayPush(result, getSymbols(object));
    object = getPrototype(object);
  }
  return result;
};

module.exports = getSymbolsIn;

},{"./_arrayPush":197,"./_getPrototype":277,"./_getSymbols":279,"./stubArray":373}],281:[function(_dereq_,module,exports){
arguments[4][98][0].apply(exports,arguments)
},{"./_DataView":180,"./_Map":183,"./_Promise":185,"./_Set":186,"./_WeakMap":191,"./_baseGetTag":214,"./_toSource":336,"dup":98}],282:[function(_dereq_,module,exports){
arguments[4][99][0].apply(exports,arguments)
},{"dup":99}],283:[function(_dereq_,module,exports){
arguments[4][100][0].apply(exports,arguments)
},{"./_castPath":249,"./_isIndex":293,"./_toKey":335,"./isArguments":347,"./isArray":348,"./isLength":353,"dup":100}],284:[function(_dereq_,module,exports){
arguments[4][101][0].apply(exports,arguments)
},{"./_nativeCreate":313,"dup":101}],285:[function(_dereq_,module,exports){
arguments[4][102][0].apply(exports,arguments)
},{"dup":102}],286:[function(_dereq_,module,exports){
arguments[4][103][0].apply(exports,arguments)
},{"./_nativeCreate":313,"dup":103}],287:[function(_dereq_,module,exports){
arguments[4][104][0].apply(exports,arguments)
},{"./_nativeCreate":313,"dup":104}],288:[function(_dereq_,module,exports){
arguments[4][105][0].apply(exports,arguments)
},{"./_nativeCreate":313,"dup":105}],289:[function(_dereq_,module,exports){
/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Initializes an array clone.
 *
 * @private
 * @param {Array} array The array to clone.
 * @returns {Array} Returns the initialized clone.
 */
function initCloneArray(array) {
  var length = array.length,
      result = new array.constructor(length);

  // Add properties assigned by `RegExp#exec`.
  if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
    result.index = array.index;
    result.input = array.input;
  }
  return result;
}

module.exports = initCloneArray;

},{}],290:[function(_dereq_,module,exports){
var cloneArrayBuffer = _dereq_('./_cloneArrayBuffer'),
    cloneDataView = _dereq_('./_cloneDataView'),
    cloneRegExp = _dereq_('./_cloneRegExp'),
    cloneSymbol = _dereq_('./_cloneSymbol'),
    cloneTypedArray = _dereq_('./_cloneTypedArray');

/** `Object#toString` result references. */
var boolTag = '[object Boolean]',
    dateTag = '[object Date]',
    mapTag = '[object Map]',
    numberTag = '[object Number]',
    regexpTag = '[object RegExp]',
    setTag = '[object Set]',
    stringTag = '[object String]',
    symbolTag = '[object Symbol]';

var arrayBufferTag = '[object ArrayBuffer]',
    dataViewTag = '[object DataView]',
    float32Tag = '[object Float32Array]',
    float64Tag = '[object Float64Array]',
    int8Tag = '[object Int8Array]',
    int16Tag = '[object Int16Array]',
    int32Tag = '[object Int32Array]',
    uint8Tag = '[object Uint8Array]',
    uint8ClampedTag = '[object Uint8ClampedArray]',
    uint16Tag = '[object Uint16Array]',
    uint32Tag = '[object Uint32Array]';

/**
 * Initializes an object clone based on its `toStringTag`.
 *
 * **Note:** This function only supports cloning values with tags of
 * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.
 *
 * @private
 * @param {Object} object The object to clone.
 * @param {string} tag The `toStringTag` of the object to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the initialized clone.
 */
function initCloneByTag(object, tag, isDeep) {
  var Ctor = object.constructor;
  switch (tag) {
    case arrayBufferTag:
      return cloneArrayBuffer(object);

    case boolTag:
    case dateTag:
      return new Ctor(+object);

    case dataViewTag:
      return cloneDataView(object, isDeep);

    case float32Tag: case float64Tag:
    case int8Tag: case int16Tag: case int32Tag:
    case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
      return cloneTypedArray(object, isDeep);

    case mapTag:
      return new Ctor;

    case numberTag:
    case stringTag:
      return new Ctor(object);

    case regexpTag:
      return cloneRegExp(object);

    case setTag:
      return new Ctor;

    case symbolTag:
      return cloneSymbol(object);
  }
}

module.exports = initCloneByTag;

},{"./_cloneArrayBuffer":250,"./_cloneDataView":252,"./_cloneRegExp":253,"./_cloneSymbol":254,"./_cloneTypedArray":255}],291:[function(_dereq_,module,exports){
var baseCreate = _dereq_('./_baseCreate'),
    getPrototype = _dereq_('./_getPrototype'),
    isPrototype = _dereq_('./_isPrototype');

/**
 * Initializes an object clone.
 *
 * @private
 * @param {Object} object The object to clone.
 * @returns {Object} Returns the initialized clone.
 */
function initCloneObject(object) {
  return (typeof object.constructor == 'function' && !isPrototype(object))
    ? baseCreate(getPrototype(object))
    : {};
}

module.exports = initCloneObject;

},{"./_baseCreate":206,"./_getPrototype":277,"./_isPrototype":298}],292:[function(_dereq_,module,exports){
var Symbol = _dereq_('./_Symbol'),
    isArguments = _dereq_('./isArguments'),
    isArray = _dereq_('./isArray');

/** Built-in value references. */
var spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined;

/**
 * Checks if `value` is a flattenable `arguments` object or array.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
 */
function isFlattenable(value) {
  return isArray(value) || isArguments(value) ||
    !!(spreadableSymbol && value && value[spreadableSymbol]);
}

module.exports = isFlattenable;

},{"./_Symbol":189,"./isArguments":347,"./isArray":348}],293:[function(_dereq_,module,exports){
arguments[4][106][0].apply(exports,arguments)
},{"dup":106}],294:[function(_dereq_,module,exports){
var eq = _dereq_('./eq'),
    isArrayLike = _dereq_('./isArrayLike'),
    isIndex = _dereq_('./_isIndex'),
    isObject = _dereq_('./isObject');

/**
 * Checks if the given arguments are from an iteratee call.
 *
 * @private
 * @param {*} value The potential iteratee value argument.
 * @param {*} index The potential iteratee index or key argument.
 * @param {*} object The potential iteratee object argument.
 * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
 *  else `false`.
 */
function isIterateeCall(value, index, object) {
  if (!isObject(object)) {
    return false;
  }
  var type = typeof index;
  if (type == 'number'
        ? (isArrayLike(object) && isIndex(index, object.length))
        : (type == 'string' && index in object)
      ) {
    return eq(object[index], value);
  }
  return false;
}

module.exports = isIterateeCall;

},{"./_isIndex":293,"./eq":339,"./isArrayLike":349,"./isObject":355}],295:[function(_dereq_,module,exports){
arguments[4][107][0].apply(exports,arguments)
},{"./isArray":348,"./isSymbol":360,"dup":107}],296:[function(_dereq_,module,exports){
arguments[4][108][0].apply(exports,arguments)
},{"dup":108}],297:[function(_dereq_,module,exports){
arguments[4][109][0].apply(exports,arguments)
},{"./_coreJsData":260,"dup":109}],298:[function(_dereq_,module,exports){
arguments[4][110][0].apply(exports,arguments)
},{"dup":110}],299:[function(_dereq_,module,exports){
arguments[4][111][0].apply(exports,arguments)
},{"./isObject":355,"dup":111}],300:[function(_dereq_,module,exports){
arguments[4][112][0].apply(exports,arguments)
},{"dup":112}],301:[function(_dereq_,module,exports){
arguments[4][113][0].apply(exports,arguments)
},{"./_assocIndexOf":201,"dup":113}],302:[function(_dereq_,module,exports){
arguments[4][114][0].apply(exports,arguments)
},{"./_assocIndexOf":201,"dup":114}],303:[function(_dereq_,module,exports){
arguments[4][115][0].apply(exports,arguments)
},{"./_assocIndexOf":201,"dup":115}],304:[function(_dereq_,module,exports){
arguments[4][116][0].apply(exports,arguments)
},{"./_assocIndexOf":201,"dup":116}],305:[function(_dereq_,module,exports){
arguments[4][117][0].apply(exports,arguments)
},{"./_Hash":181,"./_ListCache":182,"./_Map":183,"dup":117}],306:[function(_dereq_,module,exports){
arguments[4][118][0].apply(exports,arguments)
},{"./_getMapData":274,"dup":118}],307:[function(_dereq_,module,exports){
arguments[4][119][0].apply(exports,arguments)
},{"./_getMapData":274,"dup":119}],308:[function(_dereq_,module,exports){
arguments[4][120][0].apply(exports,arguments)
},{"./_getMapData":274,"dup":120}],309:[function(_dereq_,module,exports){
arguments[4][121][0].apply(exports,arguments)
},{"./_getMapData":274,"dup":121}],310:[function(_dereq_,module,exports){
arguments[4][122][0].apply(exports,arguments)
},{"dup":122}],311:[function(_dereq_,module,exports){
arguments[4][123][0].apply(exports,arguments)
},{"dup":123}],312:[function(_dereq_,module,exports){
arguments[4][124][0].apply(exports,arguments)
},{"./memoize":366,"dup":124}],313:[function(_dereq_,module,exports){
arguments[4][125][0].apply(exports,arguments)
},{"./_getNative":276,"dup":125}],314:[function(_dereq_,module,exports){
arguments[4][126][0].apply(exports,arguments)
},{"./_overArg":318,"dup":126}],315:[function(_dereq_,module,exports){
/**
 * This function is like
 * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
 * except that it includes inherited enumerable properties.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 */
function nativeKeysIn(object) {
  var result = [];
  if (object != null) {
    for (var key in Object(object)) {
      result.push(key);
    }
  }
  return result;
}

module.exports = nativeKeysIn;

},{}],316:[function(_dereq_,module,exports){
var freeGlobal = _dereq_('./_freeGlobal');

/** Detect free variable `exports`. */
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;

/** Detect free variable `module`. */
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;

/** Detect the popular CommonJS extension `module.exports`. */
var moduleExports = freeModule && freeModule.exports === freeExports;

/** Detect free variable `process` from Node.js. */
var freeProcess = moduleExports && freeGlobal.process;

/** Used to access faster Node.js helpers. */
var nodeUtil = (function() {
  try {
    return freeProcess && freeProcess.binding && freeProcess.binding('util');
  } catch (e) {}
}());

module.exports = nodeUtil;

},{"./_freeGlobal":271}],317:[function(_dereq_,module,exports){
arguments[4][128][0].apply(exports,arguments)
},{"dup":128}],318:[function(_dereq_,module,exports){
arguments[4][129][0].apply(exports,arguments)
},{"dup":129}],319:[function(_dereq_,module,exports){
var apply = _dereq_('./_apply');

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;

/**
 * A specialized version of `baseRest` which transforms the rest array.
 *
 * @private
 * @param {Function} func The function to apply a rest parameter to.
 * @param {number} [start=func.length-1] The start position of the rest parameter.
 * @param {Function} transform The rest array transform.
 * @returns {Function} Returns the new function.
 */
function overRest(func, start, transform) {
  start = nativeMax(start === undefined ? (func.length - 1) : start, 0);
  return function() {
    var args = arguments,
        index = -1,
        length = nativeMax(args.length - start, 0),
        array = Array(length);

    while (++index < length) {
      array[index] = args[start + index];
    }
    index = -1;
    var otherArgs = Array(start + 1);
    while (++index < start) {
      otherArgs[index] = args[index];
    }
    otherArgs[start] = transform(array);
    return apply(func, this, otherArgs);
  };
}

module.exports = overRest;

},{"./_apply":192}],320:[function(_dereq_,module,exports){
var baseGet = _dereq_('./_baseGet'),
    baseSlice = _dereq_('./_baseSlice');

/**
 * Gets the parent value at `path` of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {Array} path The path to get the parent value of.
 * @returns {*} Returns the parent value.
 */
function parent(object, path) {
  return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1));
}

module.exports = parent;

},{"./_baseGet":212,"./_baseSlice":241}],321:[function(_dereq_,module,exports){
arguments[4][130][0].apply(exports,arguments)
},{"./_freeGlobal":271,"dup":130}],322:[function(_dereq_,module,exports){
/**
 * Gets the value at `key`, unless `key` is "__proto__".
 *
 * @private
 * @param {Object} object The object to query.
 * @param {string} key The key of the property to get.
 * @returns {*} Returns the property value.
 */
function safeGet(object, key) {
  return key == '__proto__'
    ? undefined
    : object[key];
}

module.exports = safeGet;

},{}],323:[function(_dereq_,module,exports){
arguments[4][131][0].apply(exports,arguments)
},{"dup":131}],324:[function(_dereq_,module,exports){
arguments[4][132][0].apply(exports,arguments)
},{"dup":132}],325:[function(_dereq_,module,exports){
arguments[4][133][0].apply(exports,arguments)
},{"dup":133}],326:[function(_dereq_,module,exports){
var baseSetToString = _dereq_('./_baseSetToString'),
    shortOut = _dereq_('./_shortOut');

/**
 * Sets the `toString` method of `func` to return `string`.
 *
 * @private
 * @param {Function} func The function to modify.
 * @param {Function} string The `toString` result.
 * @returns {Function} Returns `func`.
 */
var setToString = shortOut(baseSetToString);

module.exports = setToString;

},{"./_baseSetToString":240,"./_shortOut":327}],327:[function(_dereq_,module,exports){
/** Used to detect hot functions by number of calls within a span of milliseconds. */
var HOT_COUNT = 800,
    HOT_SPAN = 16;

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeNow = Date.now;

/**
 * Creates a function that'll short out and invoke `identity` instead
 * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`
 * milliseconds.
 *
 * @private
 * @param {Function} func The function to restrict.
 * @returns {Function} Returns the new shortable function.
 */
function shortOut(func) {
  var count = 0,
      lastCalled = 0;

  return function() {
    var stamp = nativeNow(),
        remaining = HOT_SPAN - (stamp - lastCalled);

    lastCalled = stamp;
    if (remaining > 0) {
      if (++count >= HOT_COUNT) {
        return arguments[0];
      }
    } else {
      count = 0;
    }
    return func.apply(undefined, arguments);
  };
}

module.exports = shortOut;

},{}],328:[function(_dereq_,module,exports){
arguments[4][134][0].apply(exports,arguments)
},{"./_ListCache":182,"dup":134}],329:[function(_dereq_,module,exports){
arguments[4][135][0].apply(exports,arguments)
},{"dup":135}],330:[function(_dereq_,module,exports){
arguments[4][136][0].apply(exports,arguments)
},{"dup":136}],331:[function(_dereq_,module,exports){
arguments[4][137][0].apply(exports,arguments)
},{"dup":137}],332:[function(_dereq_,module,exports){
arguments[4][138][0].apply(exports,arguments)
},{"./_ListCache":182,"./_Map":183,"./_MapCache":184,"dup":138}],333:[function(_dereq_,module,exports){
/**
 * A specialized version of `_.indexOf` which performs strict equality
 * comparisons of values, i.e. `===`.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} value The value to search for.
 * @param {number} fromIndex The index to search from.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function strictIndexOf(array, value, fromIndex) {
  var index = fromIndex - 1,
      length = array.length;

  while (++index < length) {
    if (array[index] === value) {
      return index;
    }
  }
  return -1;
}

module.exports = strictIndexOf;

},{}],334:[function(_dereq_,module,exports){
arguments[4][139][0].apply(exports,arguments)
},{"./_memoizeCapped":312,"dup":139}],335:[function(_dereq_,module,exports){
arguments[4][140][0].apply(exports,arguments)
},{"./isSymbol":360,"dup":140}],336:[function(_dereq_,module,exports){
arguments[4][141][0].apply(exports,arguments)
},{"dup":141}],337:[function(_dereq_,module,exports){
/**
 * Creates a function that returns `value`.
 *
 * @static
 * @memberOf _
 * @since 2.4.0
 * @category Util
 * @param {*} value The value to return from the new function.
 * @returns {Function} Returns the new constant function.
 * @example
 *
 * var objects = _.times(2, _.constant({ 'a': 1 }));
 *
 * console.log(objects);
 * // => [{ 'a': 1 }, { 'a': 1 }]
 *
 * console.log(objects[0] === objects[1]);
 * // => true
 */
function constant(value) {
  return function() {
    return value;
  };
}

module.exports = constant;

},{}],338:[function(_dereq_,module,exports){
var isObject = _dereq_('./isObject'),
    now = _dereq_('./now'),
    toNumber = _dereq_('./toNumber');

/** Error message constants. */
var FUNC_ERROR_TEXT = 'Expected a function';

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max,
    nativeMin = Math.min;

/**
 * Creates a debounced function that delays invoking `func` until after `wait`
 * milliseconds have elapsed since the last time the debounced function was
 * invoked. The debounced function comes with a `cancel` method to cancel
 * delayed `func` invocations and a `flush` method to immediately invoke them.
 * Provide `options` to indicate whether `func` should be invoked on the
 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
 * with the last arguments provided to the debounced function. Subsequent
 * calls to the debounced function return the result of the last `func`
 * invocation.
 *
 * **Note:** If `leading` and `trailing` options are `true`, `func` is
 * invoked on the trailing edge of the timeout only if the debounced function
 * is invoked more than once during the `wait` timeout.
 *
 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
 *
 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
 * for details over the differences between `_.debounce` and `_.throttle`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Function
 * @param {Function} func The function to debounce.
 * @param {number} [wait=0] The number of milliseconds to delay.
 * @param {Object} [options={}] The options object.
 * @param {boolean} [options.leading=false]
 *  Specify invoking on the leading edge of the timeout.
 * @param {number} [options.maxWait]
 *  The maximum time `func` is allowed to be delayed before it's invoked.
 * @param {boolean} [options.trailing=true]
 *  Specify invoking on the trailing edge of the timeout.
 * @returns {Function} Returns the new debounced function.
 * @example
 *
 * // Avoid costly calculations while the window size is in flux.
 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
 *
 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
 * jQuery(element).on('click', _.debounce(sendMail, 300, {
 *   'leading': true,
 *   'trailing': false
 * }));
 *
 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
 * var source = new EventSource('/stream');
 * jQuery(source).on('message', debounced);
 *
 * // Cancel the trailing debounced invocation.
 * jQuery(window).on('popstate', debounced.cancel);
 */
function debounce(func, wait, options) {
  var lastArgs,
      lastThis,
      maxWait,
      result,
      timerId,
      lastCallTime,
      lastInvokeTime = 0,
      leading = false,
      maxing = false,
      trailing = true;

  if (typeof func != 'function') {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  wait = toNumber(wait) || 0;
  if (isObject(options)) {
    leading = !!options.leading;
    maxing = 'maxWait' in options;
    maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
    trailing = 'trailing' in options ? !!options.trailing : trailing;
  }

  function invokeFunc(time) {
    var args = lastArgs,
        thisArg = lastThis;

    lastArgs = lastThis = undefined;
    lastInvokeTime = time;
    result = func.apply(thisArg, args);
    return result;
  }

  function leadingEdge(time) {
    // Reset any `maxWait` timer.
    lastInvokeTime = time;
    // Start the timer for the trailing edge.
    timerId = setTimeout(timerExpired, wait);
    // Invoke the leading edge.
    return leading ? invokeFunc(time) : result;
  }

  function remainingWait(time) {
    var timeSinceLastCall = time - lastCallTime,
        timeSinceLastInvoke = time - lastInvokeTime,
        timeWaiting = wait - timeSinceLastCall;

    return maxing
      ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
      : timeWaiting;
  }

  function shouldInvoke(time) {
    var timeSinceLastCall = time - lastCallTime,
        timeSinceLastInvoke = time - lastInvokeTime;

    // Either this is the first call, activity has stopped and we're at the
    // trailing edge, the system time has gone backwards and we're treating
    // it as the trailing edge, or we've hit the `maxWait` limit.
    return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
      (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
  }

  function timerExpired() {
    var time = now();
    if (shouldInvoke(time)) {
      return trailingEdge(time);
    }
    // Restart the timer.
    timerId = setTimeout(timerExpired, remainingWait(time));
  }

  function trailingEdge(time) {
    timerId = undefined;

    // Only invoke if we have `lastArgs` which means `func` has been
    // debounced at least once.
    if (trailing && lastArgs) {
      return invokeFunc(time);
    }
    lastArgs = lastThis = undefined;
    return result;
  }

  function cancel() {
    if (timerId !== undefined) {
      clearTimeout(timerId);
    }
    lastInvokeTime = 0;
    lastArgs = lastCallTime = lastThis = timerId = undefined;
  }

  function flush() {
    return timerId === undefined ? result : trailingEdge(now());
  }

  function debounced() {
    var time = now(),
        isInvoking = shouldInvoke(time);

    lastArgs = arguments;
    lastThis = this;
    lastCallTime = time;

    if (isInvoking) {
      if (timerId === undefined) {
        return leadingEdge(lastCallTime);
      }
      if (maxing) {
        // Handle invocations in a tight loop.
        timerId = setTimeout(timerExpired, wait);
        return invokeFunc(lastCallTime);
      }
    }
    if (timerId === undefined) {
      timerId = setTimeout(timerExpired, wait);
    }
    return result;
  }
  debounced.cancel = cancel;
  debounced.flush = flush;
  return debounced;
}

module.exports = debounce;

},{"./isObject":355,"./now":368,"./toNumber":378}],339:[function(_dereq_,module,exports){
arguments[4][142][0].apply(exports,arguments)
},{"dup":142}],340:[function(_dereq_,module,exports){
arguments[4][143][0].apply(exports,arguments)
},{"./_createFind":264,"./findIndex":341,"dup":143}],341:[function(_dereq_,module,exports){
arguments[4][144][0].apply(exports,arguments)
},{"./_baseFindIndex":208,"./_baseIteratee":226,"./toInteger":377,"dup":144}],342:[function(_dereq_,module,exports){
var baseFlatten = _dereq_('./_baseFlatten');

/**
 * Flattens `array` a single level deep.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Array
 * @param {Array} array The array to flatten.
 * @returns {Array} Returns the new flattened array.
 * @example
 *
 * _.flatten([1, [2, [3, [4]], 5]]);
 * // => [1, 2, [3, [4]], 5]
 */
function flatten(array) {
  var length = array == null ? 0 : array.length;
  return length ? baseFlatten(array, 1) : [];
}

module.exports = flatten;

},{"./_baseFlatten":209}],343:[function(_dereq_,module,exports){
arguments[4][145][0].apply(exports,arguments)
},{"./_baseGet":212,"dup":145}],344:[function(_dereq_,module,exports){
arguments[4][146][0].apply(exports,arguments)
},{"./_baseHasIn":215,"./_hasPath":283,"dup":146}],345:[function(_dereq_,module,exports){
arguments[4][147][0].apply(exports,arguments)
},{"dup":147}],346:[function(_dereq_,module,exports){
var baseIndexOf = _dereq_('./_baseIndexOf'),
    isArrayLike = _dereq_('./isArrayLike'),
    isString = _dereq_('./isString'),
    toInteger = _dereq_('./toInteger'),
    values = _dereq_('./values');

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;

/**
 * Checks if `value` is in `collection`. If `collection` is a string, it's
 * checked for a substring of `value`, otherwise
 * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
 * is used for equality comparisons. If `fromIndex` is negative, it's used as
 * the offset from the end of `collection`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Collection
 * @param {Array|Object|string} collection The collection to inspect.
 * @param {*} value The value to search for.
 * @param {number} [fromIndex=0] The index to search from.
 * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
 * @returns {boolean} Returns `true` if `value` is found, else `false`.
 * @example
 *
 * _.includes([1, 2, 3], 1);
 * // => true
 *
 * _.includes([1, 2, 3], 1, 2);
 * // => false
 *
 * _.includes({ 'a': 1, 'b': 2 }, 1);
 * // => true
 *
 * _.includes('abcd', 'bc');
 * // => true
 */
function includes(collection, value, fromIndex, guard) {
  collection = isArrayLike(collection) ? collection : values(collection);
  fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0;

  var length = collection.length;
  if (fromIndex < 0) {
    fromIndex = nativeMax(length + fromIndex, 0);
  }
  return isString(collection)
    ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1)
    : (!!length && baseIndexOf(collection, value, fromIndex) > -1);
}

module.exports = includes;

},{"./_baseIndexOf":216,"./isArrayLike":349,"./isString":359,"./toInteger":377,"./values":381}],347:[function(_dereq_,module,exports){
arguments[4][148][0].apply(exports,arguments)
},{"./_baseIsArguments":217,"./isObjectLike":356,"dup":148}],348:[function(_dereq_,module,exports){
arguments[4][149][0].apply(exports,arguments)
},{"dup":149}],349:[function(_dereq_,module,exports){
arguments[4][150][0].apply(exports,arguments)
},{"./isFunction":352,"./isLength":353,"dup":150}],350:[function(_dereq_,module,exports){
var isArrayLike = _dereq_('./isArrayLike'),
    isObjectLike = _dereq_('./isObjectLike');

/**
 * This method is like `_.isArrayLike` except that it also checks if `value`
 * is an object.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an array-like object,
 *  else `false`.
 * @example
 *
 * _.isArrayLikeObject([1, 2, 3]);
 * // => true
 *
 * _.isArrayLikeObject(document.body.children);
 * // => true
 *
 * _.isArrayLikeObject('abc');
 * // => false
 *
 * _.isArrayLikeObject(_.noop);
 * // => false
 */
function isArrayLikeObject(value) {
  return isObjectLike(value) && isArrayLike(value);
}

module.exports = isArrayLikeObject;

},{"./isArrayLike":349,"./isObjectLike":356}],351:[function(_dereq_,module,exports){
arguments[4][151][0].apply(exports,arguments)
},{"./_root":321,"./stubFalse":374,"dup":151}],352:[function(_dereq_,module,exports){
arguments[4][152][0].apply(exports,arguments)
},{"./_baseGetTag":214,"./isObject":355,"dup":152}],353:[function(_dereq_,module,exports){
arguments[4][153][0].apply(exports,arguments)
},{"dup":153}],354:[function(_dereq_,module,exports){
var baseIsMap = _dereq_('./_baseIsMap'),
    baseUnary = _dereq_('./_baseUnary'),
    nodeUtil = _dereq_('./_nodeUtil');

/* Node.js helper references. */
var nodeIsMap = nodeUtil && nodeUtil.isMap;

/**
 * Checks if `value` is classified as a `Map` object.
 *
 * @static
 * @memberOf _
 * @since 4.3.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a map, else `false`.
 * @example
 *
 * _.isMap(new Map);
 * // => true
 *
 * _.isMap(new WeakMap);
 * // => false
 */
var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap;

module.exports = isMap;

},{"./_baseIsMap":220,"./_baseUnary":245,"./_nodeUtil":316}],355:[function(_dereq_,module,exports){
arguments[4][154][0].apply(exports,arguments)
},{"dup":154}],356:[function(_dereq_,module,exports){
arguments[4][155][0].apply(exports,arguments)
},{"dup":155}],357:[function(_dereq_,module,exports){
var baseGetTag = _dereq_('./_baseGetTag'),
    getPrototype = _dereq_('./_getPrototype'),
    isObjectLike = _dereq_('./isObjectLike');

/** `Object#toString` result references. */
var objectTag = '[object Object]';

/** Used for built-in method references. */
var funcProto = Function.prototype,
    objectProto = Object.prototype;

/** Used to resolve the decompiled source of functions. */
var funcToString = funcProto.toString;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/** Used to infer the `Object` constructor. */
var objectCtorString = funcToString.call(Object);

/**
 * Checks if `value` is a plain object, that is, an object created by the
 * `Object` constructor or one with a `[[Prototype]]` of `null`.
 *
 * @static
 * @memberOf _
 * @since 0.8.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 * }
 *
 * _.isPlainObject(new Foo);
 * // => false
 *
 * _.isPlainObject([1, 2, 3]);
 * // => false
 *
 * _.isPlainObject({ 'x': 0, 'y': 0 });
 * // => true
 *
 * _.isPlainObject(Object.create(null));
 * // => true
 */
function isPlainObject(value) {
  if (!isObjectLike(value) || baseGetTag(value) != objectTag) {
    return false;
  }
  var proto = getPrototype(value);
  if (proto === null) {
    return true;
  }
  var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;
  return typeof Ctor == 'function' && Ctor instanceof Ctor &&
    funcToString.call(Ctor) == objectCtorString;
}

module.exports = isPlainObject;

},{"./_baseGetTag":214,"./_getPrototype":277,"./isObjectLike":356}],358:[function(_dereq_,module,exports){
var baseIsSet = _dereq_('./_baseIsSet'),
    baseUnary = _dereq_('./_baseUnary'),
    nodeUtil = _dereq_('./_nodeUtil');

/* Node.js helper references. */
var nodeIsSet = nodeUtil && nodeUtil.isSet;

/**
 * Checks if `value` is classified as a `Set` object.
 *
 * @static
 * @memberOf _
 * @since 4.3.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a set, else `false`.
 * @example
 *
 * _.isSet(new Set);
 * // => true
 *
 * _.isSet(new WeakSet);
 * // => false
 */
var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet;

module.exports = isSet;

},{"./_baseIsSet":224,"./_baseUnary":245,"./_nodeUtil":316}],359:[function(_dereq_,module,exports){
var baseGetTag = _dereq_('./_baseGetTag'),
    isArray = _dereq_('./isArray'),
    isObjectLike = _dereq_('./isObjectLike');

/** `Object#toString` result references. */
var stringTag = '[object String]';

/**
 * Checks if `value` is classified as a `String` primitive or object.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a string, else `false`.
 * @example
 *
 * _.isString('abc');
 * // => true
 *
 * _.isString(1);
 * // => false
 */
function isString(value) {
  return typeof value == 'string' ||
    (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag);
}

module.exports = isString;

},{"./_baseGetTag":214,"./isArray":348,"./isObjectLike":356}],360:[function(_dereq_,module,exports){
arguments[4][156][0].apply(exports,arguments)
},{"./_baseGetTag":214,"./isObjectLike":356,"dup":156}],361:[function(_dereq_,module,exports){
arguments[4][157][0].apply(exports,arguments)
},{"./_baseIsTypedArray":225,"./_baseUnary":245,"./_nodeUtil":316,"dup":157}],362:[function(_dereq_,module,exports){
arguments[4][158][0].apply(exports,arguments)
},{"./_arrayLikeKeys":195,"./_baseKeys":227,"./isArrayLike":349,"dup":158}],363:[function(_dereq_,module,exports){
var arrayLikeKeys = _dereq_('./_arrayLikeKeys'),
    baseKeysIn = _dereq_('./_baseKeysIn'),
    isArrayLike = _dereq_('./isArrayLike');

/**
 * Creates an array of the own and inherited enumerable property names of `object`.
 *
 * **Note:** Non-object values are coerced to objects.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category Object
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.keysIn(new Foo);
 * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
 */
function keysIn(object) {
  return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object);
}

module.exports = keysIn;

},{"./_arrayLikeKeys":195,"./_baseKeysIn":228,"./isArrayLike":349}],364:[function(_dereq_,module,exports){
/**
 * Gets the last element of `array`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Array
 * @param {Array} array The array to query.
 * @returns {*} Returns the last element of `array`.
 * @example
 *
 * _.last([1, 2, 3]);
 * // => 3
 */
function last(array) {
  var length = array == null ? 0 : array.length;
  return length ? array[length - 1] : undefined;
}

module.exports = last;

},{}],365:[function(_dereq_,module,exports){
var arrayMap = _dereq_('./_arrayMap'),
    baseIteratee = _dereq_('./_baseIteratee'),
    baseMap = _dereq_('./_baseMap'),
    isArray = _dereq_('./isArray');

/**
 * Creates an array of values by running each element in `collection` thru
 * `iteratee`. The iteratee is invoked with three arguments:
 * (value, index|key, collection).
 *
 * Many lodash methods are guarded to work as iteratees for methods like
 * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
 *
 * The guarded methods are:
 * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
 * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
 * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
 * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Collection
 * @param {Array|Object} collection The collection to iterate over.
 * @param {Function} [iteratee=_.identity] The function invoked per iteration.
 * @returns {Array} Returns the new mapped array.
 * @example
 *
 * function square(n) {
 *   return n * n;
 * }
 *
 * _.map([4, 8], square);
 * // => [16, 64]
 *
 * _.map({ 'a': 4, 'b': 8 }, square);
 * // => [16, 64] (iteration order is not guaranteed)
 *
 * var users = [
 *   { 'user': 'barney' },
 *   { 'user': 'fred' }
 * ];
 *
 * // The `_.property` iteratee shorthand.
 * _.map(users, 'user');
 * // => ['barney', 'fred']
 */
function map(collection, iteratee) {
  var func = isArray(collection) ? arrayMap : baseMap;
  return func(collection, baseIteratee(iteratee, 3));
}

module.exports = map;

},{"./_arrayMap":196,"./_baseIteratee":226,"./_baseMap":229,"./isArray":348}],366:[function(_dereq_,module,exports){
arguments[4][159][0].apply(exports,arguments)
},{"./_MapCache":184,"dup":159}],367:[function(_dereq_,module,exports){
var baseMerge = _dereq_('./_baseMerge'),
    createAssigner = _dereq_('./_createAssigner');

/**
 * This method is like `_.assign` except that it recursively merges own and
 * inherited enumerable string keyed properties of source objects into the
 * destination object. Source properties that resolve to `undefined` are
 * skipped if a destination value exists. Array and plain object properties
 * are merged recursively. Other objects and value types are overridden by
 * assignment. Source objects are applied from left to right. Subsequent
 * sources overwrite property assignments of previous sources.
 *
 * **Note:** This method mutates `object`.
 *
 * @static
 * @memberOf _
 * @since 0.5.0
 * @category Object
 * @param {Object} object The destination object.
 * @param {...Object} [sources] The source objects.
 * @returns {Object} Returns `object`.
 * @example
 *
 * var object = {
 *   'a': [{ 'b': 2 }, { 'd': 4 }]
 * };
 *
 * var other = {
 *   'a': [{ 'c': 3 }, { 'e': 5 }]
 * };
 *
 * _.merge(object, other);
 * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
 */
var merge = createAssigner(function(object, source, srcIndex) {
  baseMerge(object, source, srcIndex);
});

module.exports = merge;

},{"./_baseMerge":232,"./_createAssigner":261}],368:[function(_dereq_,module,exports){
var root = _dereq_('./_root');

/**
 * Gets the timestamp of the number of milliseconds that have elapsed since
 * the Unix epoch (1 January 1970 00:00:00 UTC).
 *
 * @static
 * @memberOf _
 * @since 2.4.0
 * @category Date
 * @returns {number} Returns the timestamp.
 * @example
 *
 * _.defer(function(stamp) {
 *   console.log(_.now() - stamp);
 * }, _.now());
 * // => Logs the number of milliseconds it took for the deferred invocation.
 */
var now = function() {
  return root.Date.now();
};

module.exports = now;

},{"./_root":321}],369:[function(_dereq_,module,exports){
var arrayMap = _dereq_('./_arrayMap'),
    baseClone = _dereq_('./_baseClone'),
    baseUnset = _dereq_('./_baseUnset'),
    castPath = _dereq_('./_castPath'),
    copyObject = _dereq_('./_copyObject'),
    customOmitClone = _dereq_('./_customOmitClone'),
    flatRest = _dereq_('./_flatRest'),
    getAllKeysIn = _dereq_('./_getAllKeysIn');

/** Used to compose bitmasks for cloning. */
var CLONE_DEEP_FLAG = 1,
    CLONE_FLAT_FLAG = 2,
    CLONE_SYMBOLS_FLAG = 4;

/**
 * The opposite of `_.pick`; this method creates an object composed of the
 * own and inherited enumerable property paths of `object` that are not omitted.
 *
 * **Note:** This method is considerably slower than `_.pick`.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Object
 * @param {Object} object The source object.
 * @param {...(string|string[])} [paths] The property paths to omit.
 * @returns {Object} Returns the new object.
 * @example
 *
 * var object = { 'a': 1, 'b': '2', 'c': 3 };
 *
 * _.omit(object, ['a', 'c']);
 * // => { 'b': '2' }
 */
var omit = flatRest(function(object, paths) {
  var result = {};
  if (object == null) {
    return result;
  }
  var isDeep = false;
  paths = arrayMap(paths, function(path) {
    path = castPath(path, object);
    isDeep || (isDeep = path.length > 1);
    return path;
  });
  copyObject(object, getAllKeysIn(object), result);
  if (isDeep) {
    result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone);
  }
  var length = paths.length;
  while (length--) {
    baseUnset(result, paths[length]);
  }
  return result;
});

module.exports = omit;

},{"./_arrayMap":196,"./_baseClone":205,"./_baseUnset":246,"./_castPath":249,"./_copyObject":257,"./_customOmitClone":265,"./_flatRest":270,"./_getAllKeysIn":273}],370:[function(_dereq_,module,exports){
var basePick = _dereq_('./_basePick'),
    flatRest = _dereq_('./_flatRest');

/**
 * Creates an object composed of the picked `object` properties.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Object
 * @param {Object} object The source object.
 * @param {...(string|string[])} [paths] The property paths to pick.
 * @returns {Object} Returns the new object.
 * @example
 *
 * var object = { 'a': 1, 'b': '2', 'c': 3 };
 *
 * _.pick(object, ['a', 'c']);
 * // => { 'a': 1, 'c': 3 }
 */
var pick = flatRest(function(object, paths) {
  return object == null ? {} : basePick(object, paths);
});

module.exports = pick;

},{"./_basePick":234,"./_flatRest":270}],371:[function(_dereq_,module,exports){
arguments[4][160][0].apply(exports,arguments)
},{"./_baseProperty":236,"./_basePropertyDeep":237,"./_isKey":295,"./_toKey":335,"dup":160}],372:[function(_dereq_,module,exports){
var arraySome = _dereq_('./_arraySome'),
    baseIteratee = _dereq_('./_baseIteratee'),
    baseSome = _dereq_('./_baseSome'),
    isArray = _dereq_('./isArray'),
    isIterateeCall = _dereq_('./_isIterateeCall');

/**
 * Checks if `predicate` returns truthy for **any** element of `collection`.
 * Iteration is stopped once `predicate` returns truthy. The predicate is
 * invoked with three arguments: (value, index|key, collection).
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Collection
 * @param {Array|Object} collection The collection to iterate over.
 * @param {Function} [predicate=_.identity] The function invoked per iteration.
 * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
 * @returns {boolean} Returns `true` if any element passes the predicate check,
 *  else `false`.
 * @example
 *
 * _.some([null, 0, 'yes', false], Boolean);
 * // => true
 *
 * var users = [
 *   { 'user': 'barney', 'active': true },
 *   { 'user': 'fred',   'active': false }
 * ];
 *
 * // The `_.matches` iteratee shorthand.
 * _.some(users, { 'user': 'barney', 'active': false });
 * // => false
 *
 * // The `_.matchesProperty` iteratee shorthand.
 * _.some(users, ['active', false]);
 * // => true
 *
 * // The `_.property` iteratee shorthand.
 * _.some(users, 'active');
 * // => true
 */
function some(collection, predicate, guard) {
  var func = isArray(collection) ? arraySome : baseSome;
  if (guard && isIterateeCall(collection, predicate, guard)) {
    predicate = undefined;
  }
  return func(collection, baseIteratee(predicate, 3));
}

module.exports = some;

},{"./_arraySome":198,"./_baseIteratee":226,"./_baseSome":242,"./_isIterateeCall":294,"./isArray":348}],373:[function(_dereq_,module,exports){
arguments[4][161][0].apply(exports,arguments)
},{"dup":161}],374:[function(_dereq_,module,exports){
arguments[4][162][0].apply(exports,arguments)
},{"dup":162}],375:[function(_dereq_,module,exports){
var debounce = _dereq_('./debounce'),
    isObject = _dereq_('./isObject');

/** Error message constants. */
var FUNC_ERROR_TEXT = 'Expected a function';

/**
 * Creates a throttled function that only invokes `func` at most once per
 * every `wait` milliseconds. The throttled function comes with a `cancel`
 * method to cancel delayed `func` invocations and a `flush` method to
 * immediately invoke them. Provide `options` to indicate whether `func`
 * should be invoked on the leading and/or trailing edge of the `wait`
 * timeout. The `func` is invoked with the last arguments provided to the
 * throttled function. Subsequent calls to the throttled function return the
 * result of the last `func` invocation.
 *
 * **Note:** If `leading` and `trailing` options are `true`, `func` is
 * invoked on the trailing edge of the timeout only if the throttled function
 * is invoked more than once during the `wait` timeout.
 *
 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
 *
 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
 * for details over the differences between `_.throttle` and `_.debounce`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Function
 * @param {Function} func The function to throttle.
 * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
 * @param {Object} [options={}] The options object.
 * @param {boolean} [options.leading=true]
 *  Specify invoking on the leading edge of the timeout.
 * @param {boolean} [options.trailing=true]
 *  Specify invoking on the trailing edge of the timeout.
 * @returns {Function} Returns the new throttled function.
 * @example
 *
 * // Avoid excessively updating the position while scrolling.
 * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
 *
 * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
 * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
 * jQuery(element).on('click', throttled);
 *
 * // Cancel the trailing throttled invocation.
 * jQuery(window).on('popstate', throttled.cancel);
 */
function throttle(func, wait, options) {
  var leading = true,
      trailing = true;

  if (typeof func != 'function') {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  if (isObject(options)) {
    leading = 'leading' in options ? !!options.leading : leading;
    trailing = 'trailing' in options ? !!options.trailing : trailing;
  }
  return debounce(func, wait, {
    'leading': leading,
    'maxWait': wait,
    'trailing': trailing
  });
}

module.exports = throttle;

},{"./debounce":338,"./isObject":355}],376:[function(_dereq_,module,exports){
arguments[4][163][0].apply(exports,arguments)
},{"./toNumber":378,"dup":163}],377:[function(_dereq_,module,exports){
arguments[4][164][0].apply(exports,arguments)
},{"./toFinite":376,"dup":164}],378:[function(_dereq_,module,exports){
arguments[4][165][0].apply(exports,arguments)
},{"./isObject":355,"./isSymbol":360,"dup":165}],379:[function(_dereq_,module,exports){
var copyObject = _dereq_('./_copyObject'),
    keysIn = _dereq_('./keysIn');

/**
 * Converts `value` to a plain object flattening inherited enumerable string
 * keyed properties of `value` to own properties of the plain object.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {Object} Returns the converted plain object.
 * @example
 *
 * function Foo() {
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.assign({ 'a': 1 }, new Foo);
 * // => { 'a': 1, 'b': 2 }
 *
 * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
 * // => { 'a': 1, 'b': 2, 'c': 3 }
 */
function toPlainObject(value) {
  return copyObject(value, keysIn(value));
}

module.exports = toPlainObject;

},{"./_copyObject":257,"./keysIn":363}],380:[function(_dereq_,module,exports){
arguments[4][166][0].apply(exports,arguments)
},{"./_baseToString":244,"dup":166}],381:[function(_dereq_,module,exports){
var baseValues = _dereq_('./_baseValues'),
    keys = _dereq_('./keys');

/**
 * Creates an array of the own enumerable string keyed property values of `object`.
 *
 * **Note:** Non-object values are coerced to objects.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Object
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property values.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.values(new Foo);
 * // => [1, 2] (iteration order is not guaranteed)
 *
 * _.values('hi');
 * // => ['h', 'i']
 */
function values(object) {
  return object == null ? [] : baseValues(object, keys(object));
}

module.exports = values;

},{"./_baseValues":247,"./keys":362}],382:[function(_dereq_,module,exports){
(function (global){
/*! https://mths.be/punycode v1.4.1 by @mathias */
;(function(root) {

	/** Detect free variables */
	var freeExports = typeof exports == 'object' && exports &&
		!exports.nodeType && exports;
	var freeModule = typeof module == 'object' && module &&
		!module.nodeType && module;
	var freeGlobal = typeof global == 'object' && global;
	if (
		freeGlobal.global === freeGlobal ||
		freeGlobal.window === freeGlobal ||
		freeGlobal.self === freeGlobal
	) {
		root = freeGlobal;
	}

	/**
	 * The `punycode` object.
	 * @name punycode
	 * @type Object
	 */
	var punycode,

	/** Highest positive signed 32-bit float value */
	maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1

	/** Bootstring parameters */
	base = 36,
	tMin = 1,
	tMax = 26,
	skew = 38,
	damp = 700,
	initialBias = 72,
	initialN = 128, // 0x80
	delimiter = '-', // '\x2D'

	/** Regular expressions */
	regexPunycode = /^xn--/,
	regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
	regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators

	/** Error messages */
	errors = {
		'overflow': 'Overflow: input needs wider integers to process',
		'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
		'invalid-input': 'Invalid input'
	},

	/** Convenience shortcuts */
	baseMinusTMin = base - tMin,
	floor = Math.floor,
	stringFromCharCode = String.fromCharCode,

	/** Temporary variable */
	key;

	/*--------------------------------------------------------------------------*/

	/**
	 * A generic error utility function.
	 * @private
	 * @param {String} type The error type.
	 * @returns {Error} Throws a `RangeError` with the applicable error message.
	 */
	function error(type) {
		throw new RangeError(errors[type]);
	}

	/**
	 * A generic `Array#map` utility function.
	 * @private
	 * @param {Array} array The array to iterate over.
	 * @param {Function} callback The function that gets called for every array
	 * item.
	 * @returns {Array} A new array of values returned by the callback function.
	 */
	function map(array, fn) {
		var length = array.length;
		var result = [];
		while (length--) {
			result[length] = fn(array[length]);
		}
		return result;
	}

	/**
	 * A simple `Array#map`-like wrapper to work with domain name strings or email
	 * addresses.
	 * @private
	 * @param {String} domain The domain name or email address.
	 * @param {Function} callback The function that gets called for every
	 * character.
	 * @returns {Array} A new string of characters returned by the callback
	 * function.
	 */
	function mapDomain(string, fn) {
		var parts = string.split('@');
		var result = '';
		if (parts.length > 1) {
			// In email addresses, only the domain name should be punycoded. Leave
			// the local part (i.e. everything up to `@`) intact.
			result = parts[0] + '@';
			string = parts[1];
		}
		// Avoid `split(regex)` for IE8 compatibility. See #17.
		string = string.replace(regexSeparators, '\x2E');
		var labels = string.split('.');
		var encoded = map(labels, fn).join('.');
		return result + encoded;
	}

	/**
	 * Creates an array containing the numeric code points of each Unicode
	 * character in the string. While JavaScript uses UCS-2 internally,
	 * this function will convert a pair of surrogate halves (each of which
	 * UCS-2 exposes as separate characters) into a single code point,
	 * matching UTF-16.
	 * @see `punycode.ucs2.encode`
	 * @see <https://mathiasbynens.be/notes/javascript-encoding>
	 * @memberOf punycode.ucs2
	 * @name decode
	 * @param {String} string The Unicode input string (UCS-2).
	 * @returns {Array} The new array of code points.
	 */
	function ucs2decode(string) {
		var output = [],
		    counter = 0,
		    length = string.length,
		    value,
		    extra;
		while (counter < length) {
			value = string.charCodeAt(counter++);
			if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
				// high surrogate, and there is a next character
				extra = string.charCodeAt(counter++);
				if ((extra & 0xFC00) == 0xDC00) { // low surrogate
					output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
				} else {
					// unmatched surrogate; only append this code unit, in case the next
					// code unit is the high surrogate of a surrogate pair
					output.push(value);
					counter--;
				}
			} else {
				output.push(value);
			}
		}
		return output;
	}

	/**
	 * Creates a string based on an array of numeric code points.
	 * @see `punycode.ucs2.decode`
	 * @memberOf punycode.ucs2
	 * @name encode
	 * @param {Array} codePoints The array of numeric code points.
	 * @returns {String} The new Unicode string (UCS-2).
	 */
	function ucs2encode(array) {
		return map(array, function(value) {
			var output = '';
			if (value > 0xFFFF) {
				value -= 0x10000;
				output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
				value = 0xDC00 | value & 0x3FF;
			}
			output += stringFromCharCode(value);
			return output;
		}).join('');
	}

	/**
	 * Converts a basic code point into a digit/integer.
	 * @see `digitToBasic()`
	 * @private
	 * @param {Number} codePoint The basic numeric code point value.
	 * @returns {Number} The numeric value of a basic code point (for use in
	 * representing integers) in the range `0` to `base - 1`, or `base` if
	 * the code point does not represent a value.
	 */
	function basicToDigit(codePoint) {
		if (codePoint - 48 < 10) {
			return codePoint - 22;
		}
		if (codePoint - 65 < 26) {
			return codePoint - 65;
		}
		if (codePoint - 97 < 26) {
			return codePoint - 97;
		}
		return base;
	}

	/**
	 * Converts a digit/integer into a basic code point.
	 * @see `basicToDigit()`
	 * @private
	 * @param {Number} digit The numeric value of a basic code point.
	 * @returns {Number} The basic code point whose value (when used for
	 * representing integers) is `digit`, which needs to be in the range
	 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
	 * used; else, the lowercase form is used. The behavior is undefined
	 * if `flag` is non-zero and `digit` has no uppercase form.
	 */
	function digitToBasic(digit, flag) {
		//  0..25 map to ASCII a..z or A..Z
		// 26..35 map to ASCII 0..9
		return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
	}

	/**
	 * Bias adaptation function as per section 3.4 of RFC 3492.
	 * https://tools.ietf.org/html/rfc3492#section-3.4
	 * @private
	 */
	function adapt(delta, numPoints, firstTime) {
		var k = 0;
		delta = firstTime ? floor(delta / damp) : delta >> 1;
		delta += floor(delta / numPoints);
		for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
			delta = floor(delta / baseMinusTMin);
		}
		return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
	}

	/**
	 * Converts a Punycode string of ASCII-only symbols to a string of Unicode
	 * symbols.
	 * @memberOf punycode
	 * @param {String} input The Punycode string of ASCII-only symbols.
	 * @returns {String} The resulting string of Unicode symbols.
	 */
	function decode(input) {
		// Don't use UCS-2
		var output = [],
		    inputLength = input.length,
		    out,
		    i = 0,
		    n = initialN,
		    bias = initialBias,
		    basic,
		    j,
		    index,
		    oldi,
		    w,
		    k,
		    digit,
		    t,
		    /** Cached calculation results */
		    baseMinusT;

		// Handle the basic code points: let `basic` be the number of input code
		// points before the last delimiter, or `0` if there is none, then copy
		// the first basic code points to the output.

		basic = input.lastIndexOf(delimiter);
		if (basic < 0) {
			basic = 0;
		}

		for (j = 0; j < basic; ++j) {
			// if it's not a basic code point
			if (input.charCodeAt(j) >= 0x80) {
				error('not-basic');
			}
			output.push(input.charCodeAt(j));
		}

		// Main decoding loop: start just after the last delimiter if any basic code
		// points were copied; start at the beginning otherwise.

		for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {

			// `index` is the index of the next character to be consumed.
			// Decode a generalized variable-length integer into `delta`,
			// which gets added to `i`. The overflow checking is easier
			// if we increase `i` as we go, then subtract off its starting
			// value at the end to obtain `delta`.
			for (oldi = i, w = 1, k = base; /* no condition */; k += base) {

				if (index >= inputLength) {
					error('invalid-input');
				}

				digit = basicToDigit(input.charCodeAt(index++));

				if (digit >= base || digit > floor((maxInt - i) / w)) {
					error('overflow');
				}

				i += digit * w;
				t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);

				if (digit < t) {
					break;
				}

				baseMinusT = base - t;
				if (w > floor(maxInt / baseMinusT)) {
					error('overflow');
				}

				w *= baseMinusT;

			}

			out = output.length + 1;
			bias = adapt(i - oldi, out, oldi == 0);

			// `i` was supposed to wrap around from `out` to `0`,
			// incrementing `n` each time, so we'll fix that now:
			if (floor(i / out) > maxInt - n) {
				error('overflow');
			}

			n += floor(i / out);
			i %= out;

			// Insert `n` at position `i` of the output
			output.splice(i++, 0, n);

		}

		return ucs2encode(output);
	}

	/**
	 * Converts a string of Unicode symbols (e.g. a domain name label) to a
	 * Punycode string of ASCII-only symbols.
	 * @memberOf punycode
	 * @param {String} input The string of Unicode symbols.
	 * @returns {String} The resulting Punycode string of ASCII-only symbols.
	 */
	function encode(input) {
		var n,
		    delta,
		    handledCPCount,
		    basicLength,
		    bias,
		    j,
		    m,
		    q,
		    k,
		    t,
		    currentValue,
		    output = [],
		    /** `inputLength` will hold the number of code points in `input`. */
		    inputLength,
		    /** Cached calculation results */
		    handledCPCountPlusOne,
		    baseMinusT,
		    qMinusT;

		// Convert the input in UCS-2 to Unicode
		input = ucs2decode(input);

		// Cache the length
		inputLength = input.length;

		// Initialize the state
		n = initialN;
		delta = 0;
		bias = initialBias;

		// Handle the basic code points
		for (j = 0; j < inputLength; ++j) {
			currentValue = input[j];
			if (currentValue < 0x80) {
				output.push(stringFromCharCode(currentValue));
			}
		}

		handledCPCount = basicLength = output.length;

		// `handledCPCount` is the number of code points that have been handled;
		// `basicLength` is the number of basic code points.

		// Finish the basic string - if it is not empty - with a delimiter
		if (basicLength) {
			output.push(delimiter);
		}

		// Main encoding loop:
		while (handledCPCount < inputLength) {

			// All non-basic code points < n have been handled already. Find the next
			// larger one:
			for (m = maxInt, j = 0; j < inputLength; ++j) {
				currentValue = input[j];
				if (currentValue >= n && currentValue < m) {
					m = currentValue;
				}
			}

			// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
			// but guard against overflow
			handledCPCountPlusOne = handledCPCount + 1;
			if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
				error('overflow');
			}

			delta += (m - n) * handledCPCountPlusOne;
			n = m;

			for (j = 0; j < inputLength; ++j) {
				currentValue = input[j];

				if (currentValue < n && ++delta > maxInt) {
					error('overflow');
				}

				if (currentValue == n) {
					// Represent delta as a generalized variable-length integer
					for (q = delta, k = base; /* no condition */; k += base) {
						t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
						if (q < t) {
							break;
						}
						qMinusT = q - t;
						baseMinusT = base - t;
						output.push(
							stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
						);
						q = floor(qMinusT / baseMinusT);
					}

					output.push(stringFromCharCode(digitToBasic(q, 0)));
					bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
					delta = 0;
					++handledCPCount;
				}
			}

			++delta;
			++n;

		}
		return output.join('');
	}

	/**
	 * Converts a Punycode string representing a domain name or an email address
	 * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
	 * it doesn't matter if you call it on a string that has already been
	 * converted to Unicode.
	 * @memberOf punycode
	 * @param {String} input The Punycoded domain name or email address to
	 * convert to Unicode.
	 * @returns {String} The Unicode representation of the given Punycode
	 * string.
	 */
	function toUnicode(input) {
		return mapDomain(input, function(string) {
			return regexPunycode.test(string)
				? decode(string.slice(4).toLowerCase())
				: string;
		});
	}

	/**
	 * Converts a Unicode string representing a domain name or an email address to
	 * Punycode. Only the non-ASCII parts of the domain name will be converted,
	 * i.e. it doesn't matter if you call it with a domain that's already in
	 * ASCII.
	 * @memberOf punycode
	 * @param {String} input The domain name or email address to convert, as a
	 * Unicode string.
	 * @returns {String} The Punycode representation of the given domain name or
	 * email address.
	 */
	function toASCII(input) {
		return mapDomain(input, function(string) {
			return regexNonASCII.test(string)
				? 'xn--' + encode(string)
				: string;
		});
	}

	/*--------------------------------------------------------------------------*/

	/** Define the public API */
	punycode = {
		/**
		 * A string representing the current Punycode.js version number.
		 * @memberOf punycode
		 * @type String
		 */
		'version': '1.4.1',
		/**
		 * An object of methods to convert from JavaScript's internal character
		 * representation (UCS-2) to Unicode code points, and back.
		 * @see <https://mathiasbynens.be/notes/javascript-encoding>
		 * @memberOf punycode
		 * @type Object
		 */
		'ucs2': {
			'decode': ucs2decode,
			'encode': ucs2encode
		},
		'decode': decode,
		'encode': encode,
		'toASCII': toASCII,
		'toUnicode': toUnicode
	};

	/** Expose `punycode` */
	// Some AMD build optimizers, like r.js, check for specific condition patterns
	// like the following:
	if (
		typeof define == 'function' &&
		typeof define.amd == 'object' &&
		define.amd
	) {
		define('punycode', function() {
			return punycode;
		});
	} else if (freeExports && freeModule) {
		if (module.exports == freeExports) {
			// in Node.js, io.js, or RingoJS v0.8.0+
			freeModule.exports = punycode;
		} else {
			// in Narwhal or RingoJS v0.7.0-
			for (key in punycode) {
				punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
			}
		}
	} else {
		// in Rhino or a web browser
		root.punycode = punycode;
	}

}(this));

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{}],383:[function(_dereq_,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

'use strict';

// If obj.hasOwnProperty has been overridden, then calling
// obj.hasOwnProperty(prop) will break.
// See: https://github.com/joyent/node/issues/1707
function hasOwnProperty(obj, prop) {
  return Object.prototype.hasOwnProperty.call(obj, prop);
}

module.exports = function(qs, sep, eq, options) {
  sep = sep || '&';
  eq = eq || '=';
  var obj = {};

  if (typeof qs !== 'string' || qs.length === 0) {
    return obj;
  }

  var regexp = /\+/g;
  qs = qs.split(sep);

  var maxKeys = 1000;
  if (options && typeof options.maxKeys === 'number') {
    maxKeys = options.maxKeys;
  }

  var len = qs.length;
  // maxKeys <= 0 means that we should not limit keys count
  if (maxKeys > 0 && len > maxKeys) {
    len = maxKeys;
  }

  for (var i = 0; i < len; ++i) {
    var x = qs[i].replace(regexp, '%20'),
        idx = x.indexOf(eq),
        kstr, vstr, k, v;

    if (idx >= 0) {
      kstr = x.substr(0, idx);
      vstr = x.substr(idx + 1);
    } else {
      kstr = x;
      vstr = '';
    }

    k = decodeURIComponent(kstr);
    v = decodeURIComponent(vstr);

    if (!hasOwnProperty(obj, k)) {
      obj[k] = v;
    } else if (isArray(obj[k])) {
      obj[k].push(v);
    } else {
      obj[k] = [obj[k], v];
    }
  }

  return obj;
};

var isArray = Array.isArray || function (xs) {
  return Object.prototype.toString.call(xs) === '[object Array]';
};

},{}],384:[function(_dereq_,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

'use strict';

var stringifyPrimitive = function(v) {
  switch (typeof v) {
    case 'string':
      return v;

    case 'boolean':
      return v ? 'true' : 'false';

    case 'number':
      return isFinite(v) ? v : '';

    default:
      return '';
  }
};

module.exports = function(obj, sep, eq, name) {
  sep = sep || '&';
  eq = eq || '=';
  if (obj === null) {
    obj = undefined;
  }

  if (typeof obj === 'object') {
    return map(objectKeys(obj), function(k) {
      var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
      if (isArray(obj[k])) {
        return map(obj[k], function(v) {
          return ks + encodeURIComponent(stringifyPrimitive(v));
        }).join(sep);
      } else {
        return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
      }
    }).join(sep);

  }

  if (!name) return '';
  return encodeURIComponent(stringifyPrimitive(name)) + eq +
         encodeURIComponent(stringifyPrimitive(obj));
};

var isArray = Array.isArray || function (xs) {
  return Object.prototype.toString.call(xs) === '[object Array]';
};

function map (xs, f) {
  if (xs.map) return xs.map(f);
  var res = [];
  for (var i = 0; i < xs.length; i++) {
    res.push(f(xs[i], i));
  }
  return res;
}

var objectKeys = Object.keys || function (obj) {
  var res = [];
  for (var key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key);
  }
  return res;
};

},{}],385:[function(_dereq_,module,exports){
'use strict';

exports.decode = exports.parse = _dereq_('./decode');
exports.encode = exports.stringify = _dereq_('./encode');

},{"./decode":383,"./encode":384}],386:[function(_dereq_,module,exports){
function select(element) {
    var selectedText;

    if (element.nodeName === 'SELECT') {
        element.focus();

        selectedText = element.value;
    }
    else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
        var isReadOnly = element.hasAttribute('readonly');

        if (!isReadOnly) {
            element.setAttribute('readonly', '');
        }

        element.select();
        element.setSelectionRange(0, element.value.length);

        if (!isReadOnly) {
            element.removeAttribute('readonly');
        }

        selectedText = element.value;
    }
    else {
        if (element.hasAttribute('contenteditable')) {
            element.focus();
        }

        var selection = window.getSelection();
        var range = document.createRange();

        range.selectNodeContents(element);
        selection.removeAllRanges();
        selection.addRange(range);

        selectedText = selection.toString();
    }

    return selectedText;
}

module.exports = select;

},{}],387:[function(_dereq_,module,exports){
function E () {
  // Keep this empty so it's easier to inherit from
  // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
}

E.prototype = {
  on: function (name, callback, ctx) {
    var e = this.e || (this.e = {});

    (e[name] || (e[name] = [])).push({
      fn: callback,
      ctx: ctx
    });

    return this;
  },

  once: function (name, callback, ctx) {
    var self = this;
    function listener () {
      self.off(name, listener);
      callback.apply(ctx, arguments);
    };

    listener._ = callback
    return this.on(name, listener, ctx);
  },

  emit: function (name) {
    var data = [].slice.call(arguments, 1);
    var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
    var i = 0;
    var len = evtArr.length;

    for (i; i < len; i++) {
      evtArr[i].fn.apply(evtArr[i].ctx, data);
    }

    return this;
  },

  off: function (name, callback) {
    var e = this.e || (this.e = {});
    var evts = e[name];
    var liveEvents = [];

    if (evts && callback) {
      for (var i = 0, len = evts.length; i < len; i++) {
        if (evts[i].fn !== callback && evts[i].fn._ !== callback)
          liveEvents.push(evts[i]);
      }
    }

    // Remove event from queue to prevent memory leak
    // Suggested by https://github.com/lazd
    // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910

    (liveEvents.length)
      ? e[name] = liveEvents
      : delete e[name];

    return this;
  }
};

module.exports = E;
module.exports.TinyEmitter = E;

},{}],388:[function(_dereq_,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

'use strict';

var punycode = _dereq_('punycode');
var util = _dereq_('./util');

exports.parse = urlParse;
exports.resolve = urlResolve;
exports.resolveObject = urlResolveObject;
exports.format = urlFormat;

exports.Url = Url;

function Url() {
  this.protocol = null;
  this.slashes = null;
  this.auth = null;
  this.host = null;
  this.port = null;
  this.hostname = null;
  this.hash = null;
  this.search = null;
  this.query = null;
  this.pathname = null;
  this.path = null;
  this.href = null;
}

// Reference: RFC 3986, RFC 1808, RFC 2396

// define these here so at least they only have to be
// compiled once on the first module load.
var protocolPattern = /^([a-z0-9.+-]+:)/i,
    portPattern = /:[0-9]*$/,

    // Special case for a simple path URL
    simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,

    // RFC 2396: characters reserved for delimiting URLs.
    // We actually just auto-escape these.
    delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'],

    // RFC 2396: characters not allowed for various reasons.
    unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims),

    // Allowed by RFCs, but cause of XSS attacks.  Always escape these.
    autoEscape = ['\''].concat(unwise),
    // Characters that are never ever allowed in a hostname.
    // Note that any invalid chars are also handled, but these
    // are the ones that are *expected* to be seen, so we fast-path
    // them.
    nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape),
    hostEndingChars = ['/', '?', '#'],
    hostnameMaxLen = 255,
    hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/,
    hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/,
    // protocols that can allow "unsafe" and "unwise" chars.
    unsafeProtocol = {
      'javascript': true,
      'javascript:': true
    },
    // protocols that never have a hostname.
    hostlessProtocol = {
      'javascript': true,
      'javascript:': true
    },
    // protocols that always contain a // bit.
    slashedProtocol = {
      'http': true,
      'https': true,
      'ftp': true,
      'gopher': true,
      'file': true,
      'http:': true,
      'https:': true,
      'ftp:': true,
      'gopher:': true,
      'file:': true
    },
    querystring = _dereq_('querystring');

function urlParse(url, parseQueryString, slashesDenoteHost) {
  if (url && util.isObject(url) && url instanceof Url) return url;

  var u = new Url;
  u.parse(url, parseQueryString, slashesDenoteHost);
  return u;
}

Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
  if (!util.isString(url)) {
    throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
  }

  // Copy chrome, IE, opera backslash-handling behavior.
  // Back slashes before the query string get converted to forward slashes
  // See: https://code.google.com/p/chromium/issues/detail?id=25916
  var queryIndex = url.indexOf('?'),
      splitter =
          (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#',
      uSplit = url.split(splitter),
      slashRegex = /\\/g;
  uSplit[0] = uSplit[0].replace(slashRegex, '/');
  url = uSplit.join(splitter);

  var rest = url;

  // trim before proceeding.
  // This is to support parse stuff like "  http://foo.com  \n"
  rest = rest.trim();

  if (!slashesDenoteHost && url.split('#').length === 1) {
    // Try fast path regexp
    var simplePath = simplePathPattern.exec(rest);
    if (simplePath) {
      this.path = rest;
      this.href = rest;
      this.pathname = simplePath[1];
      if (simplePath[2]) {
        this.search = simplePath[2];
        if (parseQueryString) {
          this.query = querystring.parse(this.search.substr(1));
        } else {
          this.query = this.search.substr(1);
        }
      } else if (parseQueryString) {
        this.search = '';
        this.query = {};
      }
      return this;
    }
  }

  var proto = protocolPattern.exec(rest);
  if (proto) {
    proto = proto[0];
    var lowerProto = proto.toLowerCase();
    this.protocol = lowerProto;
    rest = rest.substr(proto.length);
  }

  // figure out if it's got a host
  // user@server is *always* interpreted as a hostname, and url
  // resolution will treat //foo/bar as host=foo,path=bar because that's
  // how the browser resolves relative URLs.
  if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
    var slashes = rest.substr(0, 2) === '//';
    if (slashes && !(proto && hostlessProtocol[proto])) {
      rest = rest.substr(2);
      this.slashes = true;
    }
  }

  if (!hostlessProtocol[proto] &&
      (slashes || (proto && !slashedProtocol[proto]))) {

    // there's a hostname.
    // the first instance of /, ?, ;, or # ends the host.
    //
    // If there is an @ in the hostname, then non-host chars *are* allowed
    // to the left of the last @ sign, unless some host-ending character
    // comes *before* the @-sign.
    // URLs are obnoxious.
    //
    // ex:
    // http://a@b@c/ => user:a@b host:c
    // http://a@b?@c => user:a host:c path:/?@c

    // v0.12 TODO(isaacs): This is not quite how Chrome does things.
    // Review our test case against browsers more comprehensively.

    // find the first instance of any hostEndingChars
    var hostEnd = -1;
    for (var i = 0; i < hostEndingChars.length; i++) {
      var hec = rest.indexOf(hostEndingChars[i]);
      if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
        hostEnd = hec;
    }

    // at this point, either we have an explicit point where the
    // auth portion cannot go past, or the last @ char is the decider.
    var auth, atSign;
    if (hostEnd === -1) {
      // atSign can be anywhere.
      atSign = rest.lastIndexOf('@');
    } else {
      // atSign must be in auth portion.
      // http://a@b/c@d => host:b auth:a path:/c@d
      atSign = rest.lastIndexOf('@', hostEnd);
    }

    // Now we have a portion which is definitely the auth.
    // Pull that off.
    if (atSign !== -1) {
      auth = rest.slice(0, atSign);
      rest = rest.slice(atSign + 1);
      this.auth = decodeURIComponent(auth);
    }

    // the host is the remaining to the left of the first non-host char
    hostEnd = -1;
    for (var i = 0; i < nonHostChars.length; i++) {
      var hec = rest.indexOf(nonHostChars[i]);
      if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
        hostEnd = hec;
    }
    // if we still have not hit it, then the entire thing is a host.
    if (hostEnd === -1)
      hostEnd = rest.length;

    this.host = rest.slice(0, hostEnd);
    rest = rest.slice(hostEnd);

    // pull out port.
    this.parseHost();

    // we've indicated that there is a hostname,
    // so even if it's empty, it has to be present.
    this.hostname = this.hostname || '';

    // if hostname begins with [ and ends with ]
    // assume that it's an IPv6 address.
    var ipv6Hostname = this.hostname[0] === '[' &&
        this.hostname[this.hostname.length - 1] === ']';

    // validate a little.
    if (!ipv6Hostname) {
      var hostparts = this.hostname.split(/\./);
      for (var i = 0, l = hostparts.length; i < l; i++) {
        var part = hostparts[i];
        if (!part) continue;
        if (!part.match(hostnamePartPattern)) {
          var newpart = '';
          for (var j = 0, k = part.length; j < k; j++) {
            if (part.charCodeAt(j) > 127) {
              // we replace non-ASCII char with a temporary placeholder
              // we need this to make sure size of hostname is not
              // broken by replacing non-ASCII by nothing
              newpart += 'x';
            } else {
              newpart += part[j];
            }
          }
          // we test again with ASCII char only
          if (!newpart.match(hostnamePartPattern)) {
            var validParts = hostparts.slice(0, i);
            var notHost = hostparts.slice(i + 1);
            var bit = part.match(hostnamePartStart);
            if (bit) {
              validParts.push(bit[1]);
              notHost.unshift(bit[2]);
            }
            if (notHost.length) {
              rest = '/' + notHost.join('.') + rest;
            }
            this.hostname = validParts.join('.');
            break;
          }
        }
      }
    }

    if (this.hostname.length > hostnameMaxLen) {
      this.hostname = '';
    } else {
      // hostnames are always lower case.
      this.hostname = this.hostname.toLowerCase();
    }

    if (!ipv6Hostname) {
      // IDNA Support: Returns a punycoded representation of "domain".
      // It only converts parts of the domain name that
      // have non-ASCII characters, i.e. it doesn't matter if
      // you call it with a domain that already is ASCII-only.
      this.hostname = punycode.toASCII(this.hostname);
    }

    var p = this.port ? ':' + this.port : '';
    var h = this.hostname || '';
    this.host = h + p;
    this.href += this.host;

    // strip [ and ] from the hostname
    // the host field still retains them, though
    if (ipv6Hostname) {
      this.hostname = this.hostname.substr(1, this.hostname.length - 2);
      if (rest[0] !== '/') {
        rest = '/' + rest;
      }
    }
  }

  // now rest is set to the post-host stuff.
  // chop off any delim chars.
  if (!unsafeProtocol[lowerProto]) {

    // First, make 100% sure that any "autoEscape" chars get
    // escaped, even if encodeURIComponent doesn't think they
    // need to be.
    for (var i = 0, l = autoEscape.length; i < l; i++) {
      var ae = autoEscape[i];
      if (rest.indexOf(ae) === -1)
        continue;
      var esc = encodeURIComponent(ae);
      if (esc === ae) {
        esc = escape(ae);
      }
      rest = rest.split(ae).join(esc);
    }
  }


  // chop off from the tail first.
  var hash = rest.indexOf('#');
  if (hash !== -1) {
    // got a fragment string.
    this.hash = rest.substr(hash);
    rest = rest.slice(0, hash);
  }
  var qm = rest.indexOf('?');
  if (qm !== -1) {
    this.search = rest.substr(qm);
    this.query = rest.substr(qm + 1);
    if (parseQueryString) {
      this.query = querystring.parse(this.query);
    }
    rest = rest.slice(0, qm);
  } else if (parseQueryString) {
    // no query string, but parseQueryString still requested
    this.search = '';
    this.query = {};
  }
  if (rest) this.pathname = rest;
  if (slashedProtocol[lowerProto] &&
      this.hostname && !this.pathname) {
    this.pathname = '/';
  }

  //to support http.request
  if (this.pathname || this.search) {
    var p = this.pathname || '';
    var s = this.search || '';
    this.path = p + s;
  }

  // finally, reconstruct the href based on what has been validated.
  this.href = this.format();
  return this;
};

// format a parsed object into a url string
function urlFormat(obj) {
  // ensure it's an object, and not a string url.
  // If it's an obj, this is a no-op.
  // this way, you can call url_format() on strings
  // to clean up potentially wonky urls.
  if (util.isString(obj)) obj = urlParse(obj);
  if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
  return obj.format();
}

Url.prototype.format = function() {
  var auth = this.auth || '';
  if (auth) {
    auth = encodeURIComponent(auth);
    auth = auth.replace(/%3A/i, ':');
    auth += '@';
  }

  var protocol = this.protocol || '',
      pathname = this.pathname || '',
      hash = this.hash || '',
      host = false,
      query = '';

  if (this.host) {
    host = auth + this.host;
  } else if (this.hostname) {
    host = auth + (this.hostname.indexOf(':') === -1 ?
        this.hostname :
        '[' + this.hostname + ']');
    if (this.port) {
      host += ':' + this.port;
    }
  }

  if (this.query &&
      util.isObject(this.query) &&
      Object.keys(this.query).length) {
    query = querystring.stringify(this.query);
  }

  var search = this.search || (query && ('?' + query)) || '';

  if (protocol && protocol.substr(-1) !== ':') protocol += ':';

  // only the slashedProtocols get the //.  Not mailto:, xmpp:, etc.
  // unless they had them to begin with.
  if (this.slashes ||
      (!protocol || slashedProtocol[protocol]) && host !== false) {
    host = '//' + (host || '');
    if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
  } else if (!host) {
    host = '';
  }

  if (hash && hash.charAt(0) !== '#') hash = '#' + hash;
  if (search && search.charAt(0) !== '?') search = '?' + search;

  pathname = pathname.replace(/[?#]/g, function(match) {
    return encodeURIComponent(match);
  });
  search = search.replace('#', '%23');

  return protocol + host + pathname + search + hash;
};

function urlResolve(source, relative) {
  return urlParse(source, false, true).resolve(relative);
}

Url.prototype.resolve = function(relative) {
  return this.resolveObject(urlParse(relative, false, true)).format();
};

function urlResolveObject(source, relative) {
  if (!source) return relative;
  return urlParse(source, false, true).resolveObject(relative);
}

Url.prototype.resolveObject = function(relative) {
  if (util.isString(relative)) {
    var rel = new Url();
    rel.parse(relative, false, true);
    relative = rel;
  }

  var result = new Url();
  var tkeys = Object.keys(this);
  for (var tk = 0; tk < tkeys.length; tk++) {
    var tkey = tkeys[tk];
    result[tkey] = this[tkey];
  }

  // hash is always overridden, no matter what.
  // even href="" will remove it.
  result.hash = relative.hash;

  // if the relative url is empty, then there's nothing left to do here.
  if (relative.href === '') {
    result.href = result.format();
    return result;
  }

  // hrefs like //foo/bar always cut to the protocol.
  if (relative.slashes && !relative.protocol) {
    // take everything except the protocol from relative
    var rkeys = Object.keys(relative);
    for (var rk = 0; rk < rkeys.length; rk++) {
      var rkey = rkeys[rk];
      if (rkey !== 'protocol')
        result[rkey] = relative[rkey];
    }

    //urlParse appends trailing / to urls like http://www.example.com
    if (slashedProtocol[result.protocol] &&
        result.hostname && !result.pathname) {
      result.path = result.pathname = '/';
    }

    result.href = result.format();
    return result;
  }

  if (relative.protocol && relative.protocol !== result.protocol) {
    // if it's a known url protocol, then changing
    // the protocol does weird things
    // first, if it's not file:, then we MUST have a host,
    // and if there was a path
    // to begin with, then we MUST have a path.
    // if it is file:, then the host is dropped,
    // because that's known to be hostless.
    // anything else is assumed to be absolute.
    if (!slashedProtocol[relative.protocol]) {
      var keys = Object.keys(relative);
      for (var v = 0; v < keys.length; v++) {
        var k = keys[v];
        result[k] = relative[k];
      }
      result.href = result.format();
      return result;
    }

    result.protocol = relative.protocol;
    if (!relative.host && !hostlessProtocol[relative.protocol]) {
      var relPath = (relative.pathname || '').split('/');
      while (relPath.length && !(relative.host = relPath.shift()));
      if (!relative.host) relative.host = '';
      if (!relative.hostname) relative.hostname = '';
      if (relPath[0] !== '') relPath.unshift('');
      if (relPath.length < 2) relPath.unshift('');
      result.pathname = relPath.join('/');
    } else {
      result.pathname = relative.pathname;
    }
    result.search = relative.search;
    result.query = relative.query;
    result.host = relative.host || '';
    result.auth = relative.auth;
    result.hostname = relative.hostname || relative.host;
    result.port = relative.port;
    // to support http.request
    if (result.pathname || result.search) {
      var p = result.pathname || '';
      var s = result.search || '';
      result.path = p + s;
    }
    result.slashes = result.slashes || relative.slashes;
    result.href = result.format();
    return result;
  }

  var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
      isRelAbs = (
          relative.host ||
          relative.pathname && relative.pathname.charAt(0) === '/'
      ),
      mustEndAbs = (isRelAbs || isSourceAbs ||
                    (result.host && relative.pathname)),
      removeAllDots = mustEndAbs,
      srcPath = result.pathname && result.pathname.split('/') || [],
      relPath = relative.pathname && relative.pathname.split('/') || [],
      psychotic = result.protocol && !slashedProtocol[result.protocol];

  // if the url is a non-slashed url, then relative
  // links like ../.. should be able
  // to crawl up to the hostname, as well.  This is strange.
  // result.protocol has already been set by now.
  // Later on, put the first path part into the host field.
  if (psychotic) {
    result.hostname = '';
    result.port = null;
    if (result.host) {
      if (srcPath[0] === '') srcPath[0] = result.host;
      else srcPath.unshift(result.host);
    }
    result.host = '';
    if (relative.protocol) {
      relative.hostname = null;
      relative.port = null;
      if (relative.host) {
        if (relPath[0] === '') relPath[0] = relative.host;
        else relPath.unshift(relative.host);
      }
      relative.host = null;
    }
    mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
  }

  if (isRelAbs) {
    // it's absolute.
    result.host = (relative.host || relative.host === '') ?
                  relative.host : result.host;
    result.hostname = (relative.hostname || relative.hostname === '') ?
                      relative.hostname : result.hostname;
    result.search = relative.search;
    result.query = relative.query;
    srcPath = relPath;
    // fall through to the dot-handling below.
  } else if (relPath.length) {
    // it's relative
    // throw away the existing file, and take the new path instead.
    if (!srcPath) srcPath = [];
    srcPath.pop();
    srcPath = srcPath.concat(relPath);
    result.search = relative.search;
    result.query = relative.query;
  } else if (!util.isNullOrUndefined(relative.search)) {
    // just pull out the search.
    // like href='?foo'.
    // Put this after the other two cases because it simplifies the booleans
    if (psychotic) {
      result.hostname = result.host = srcPath.shift();
      //occationaly the auth can get stuck only in host
      //this especially happens in cases like
      //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
      var authInHost = result.host && result.host.indexOf('@') > 0 ?
                       result.host.split('@') : false;
      if (authInHost) {
        result.auth = authInHost.shift();
        result.host = result.hostname = authInHost.shift();
      }
    }
    result.search = relative.search;
    result.query = relative.query;
    //to support http.request
    if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
      result.path = (result.pathname ? result.pathname : '') +
                    (result.search ? result.search : '');
    }
    result.href = result.format();
    return result;
  }

  if (!srcPath.length) {
    // no path at all.  easy.
    // we've already handled the other stuff above.
    result.pathname = null;
    //to support http.request
    if (result.search) {
      result.path = '/' + result.search;
    } else {
      result.path = null;
    }
    result.href = result.format();
    return result;
  }

  // if a url ENDs in . or .., then it must get a trailing slash.
  // however, if it ends in anything else non-slashy,
  // then it must NOT get a trailing slash.
  var last = srcPath.slice(-1)[0];
  var hasTrailingSlash = (
      (result.host || relative.host || srcPath.length > 1) &&
      (last === '.' || last === '..') || last === '');

  // strip single dots, resolve double dots to parent dir
  // if the path tries to go above the root, `up` ends up > 0
  var up = 0;
  for (var i = srcPath.length; i >= 0; i--) {
    last = srcPath[i];
    if (last === '.') {
      srcPath.splice(i, 1);
    } else if (last === '..') {
      srcPath.splice(i, 1);
      up++;
    } else if (up) {
      srcPath.splice(i, 1);
      up--;
    }
  }

  // if the path is allowed to go above the root, restore leading ..s
  if (!mustEndAbs && !removeAllDots) {
    for (; up--; up) {
      srcPath.unshift('..');
    }
  }

  if (mustEndAbs && srcPath[0] !== '' &&
      (!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
    srcPath.unshift('');
  }

  if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {
    srcPath.push('');
  }

  var isAbsolute = srcPath[0] === '' ||
      (srcPath[0] && srcPath[0].charAt(0) === '/');

  // put the host back
  if (psychotic) {
    result.hostname = result.host = isAbsolute ? '' :
                                    srcPath.length ? srcPath.shift() : '';
    //occationaly the auth can get stuck only in host
    //this especially happens in cases like
    //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
    var authInHost = result.host && result.host.indexOf('@') > 0 ?
                     result.host.split('@') : false;
    if (authInHost) {
      result.auth = authInHost.shift();
      result.host = result.hostname = authInHost.shift();
    }
  }

  mustEndAbs = mustEndAbs || (result.host && srcPath.length);

  if (mustEndAbs && !isAbsolute) {
    srcPath.unshift('');
  }

  if (!srcPath.length) {
    result.pathname = null;
    result.path = null;
  } else {
    result.pathname = srcPath.join('/');
  }

  //to support request.http
  if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
    result.path = (result.pathname ? result.pathname : '') +
                  (result.search ? result.search : '');
  }
  result.auth = relative.auth || result.auth;
  result.slashes = result.slashes || relative.slashes;
  result.href = result.format();
  return result;
};

Url.prototype.parseHost = function() {
  var host = this.host;
  var port = portPattern.exec(host);
  if (port) {
    port = port[0];
    if (port !== ':') {
      this.port = port.substr(1);
    }
    host = host.substr(0, host.length - port.length);
  }
  if (host) this.hostname = host;
};

},{"./util":389,"punycode":382,"querystring":385}],389:[function(_dereq_,module,exports){
'use strict';

module.exports = {
  isString: function(arg) {
    return typeof(arg) === 'string';
  },
  isObject: function(arg) {
    return typeof(arg) === 'object' && arg !== null;
  },
  isNull: function(arg) {
    return arg === null;
  },
  isNullOrUndefined: function(arg) {
    return arg == null;
  }
};

},{}],390:[function(_dereq_,module,exports){
(function (global){
/**
 * @license
 * Video.js 5.10.2 <http://videojs.com/>
 * Copyright Brightcove, Inc. <https://www.brightcove.com/>
 * Available under Apache License Version 2.0
 * <https://github.com/videojs/video.js/blob/master/LICENSE>
 */

(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 _dereq_=="function"&&_dereq_;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 _dereq_=="function"&&_dereq_;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){

},{}],2:[function(_dereq_,module,exports){
'use strict';

var keys = _dereq_('object-keys');
var foreach = _dereq_('foreach');
var hasSymbols = typeof Symbol === 'function' && typeof Symbol() === 'symbol';

var toStr = Object.prototype.toString;

var isFunction = function (fn) {
	return typeof fn === 'function' && toStr.call(fn) === '[object Function]';
};

var arePropertyDescriptorsSupported = function () {
	var obj = {};
	try {
		Object.defineProperty(obj, 'x', { enumerable: false, value: obj });
        /* eslint-disable no-unused-vars, no-restricted-syntax */
        for (var _ in obj) { return false; }
        /* eslint-enable no-unused-vars, no-restricted-syntax */
		return obj.x === obj;
	} catch (e) { /* this is IE 8. */
		return false;
	}
};
var supportsDescriptors = Object.defineProperty && arePropertyDescriptorsSupported();

var defineProperty = function (object, name, value, predicate) {
	if (name in object && (!isFunction(predicate) || !predicate())) {
		return;
	}
	if (supportsDescriptors) {
		Object.defineProperty(object, name, {
			configurable: true,
			enumerable: false,
			value: value,
			writable: true
		});
	} else {
		object[name] = value;
	}
};

var defineProperties = function (object, map) {
	var predicates = arguments.length > 2 ? arguments[2] : {};
	var props = keys(map);
	if (hasSymbols) {
		props = props.concat(Object.getOwnPropertySymbols(map));
	}
	foreach(props, function (name) {
		defineProperty(object, name, map[name], predicates[name]);
	});
};

defineProperties.supportsDescriptors = !!supportsDescriptors;

module.exports = defineProperties;

},{"foreach":4,"object-keys":49}],3:[function(_dereq_,module,exports){
var isFunction = _dereq_('is-function')

module.exports = forEach

var toString = Object.prototype.toString
var hasOwnProperty = Object.prototype.hasOwnProperty

function forEach(list, iterator, context) {
    if (!isFunction(iterator)) {
        throw new TypeError('iterator must be a function')
    }

    if (arguments.length < 3) {
        context = this
    }
    
    if (toString.call(list) === '[object Array]')
        forEachArray(list, iterator, context)
    else if (typeof list === 'string')
        forEachString(list, iterator, context)
    else
        forEachObject(list, iterator, context)
}

function forEachArray(array, iterator, context) {
    for (var i = 0, len = array.length; i < len; i++) {
        if (hasOwnProperty.call(array, i)) {
            iterator.call(context, array[i], i, array)
        }
    }
}

function forEachString(string, iterator, context) {
    for (var i = 0, len = string.length; i < len; i++) {
        // no such thing as a sparse string.
        iterator.call(context, string.charAt(i), i, string)
    }
}

function forEachObject(object, iterator, context) {
    for (var k in object) {
        if (hasOwnProperty.call(object, k)) {
            iterator.call(context, object[k], k, object)
        }
    }
}

},{"is-function":9}],4:[function(_dereq_,module,exports){

var hasOwn = Object.prototype.hasOwnProperty;
var toString = Object.prototype.toString;

module.exports = function forEach (obj, fn, ctx) {
    if (toString.call(fn) !== '[object Function]') {
        throw new TypeError('iterator must be a function');
    }
    var l = obj.length;
    if (l === +l) {
        for (var i = 0; i < l; i++) {
            fn.call(ctx, obj[i], i, obj);
        }
    } else {
        for (var k in obj) {
            if (hasOwn.call(obj, k)) {
                fn.call(ctx, obj[k], k, obj);
            }
        }
    }
};


},{}],5:[function(_dereq_,module,exports){
var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible ';
var slice = Array.prototype.slice;
var toStr = Object.prototype.toString;
var funcType = '[object Function]';

module.exports = function bind(that) {
    var target = this;
    if (typeof target !== 'function' || toStr.call(target) !== funcType) {
        throw new TypeError(ERROR_MESSAGE + target);
    }
    var args = slice.call(arguments, 1);

    var bound;
    var binder = function () {
        if (this instanceof bound) {
            var result = target.apply(
                this,
                args.concat(slice.call(arguments))
            );
            if (Object(result) === result) {
                return result;
            }
            return this;
        } else {
            return target.apply(
                that,
                args.concat(slice.call(arguments))
            );
        }
    };

    var boundLength = Math.max(0, target.length - args.length);
    var boundArgs = [];
    for (var i = 0; i < boundLength; i++) {
        boundArgs.push('$' + i);
    }

    bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder);

    if (target.prototype) {
        var Empty = function Empty() {};
        Empty.prototype = target.prototype;
        bound.prototype = new Empty();
        Empty.prototype = null;
    }

    return bound;
};

},{}],6:[function(_dereq_,module,exports){
var implementation = _dereq_('./implementation');

module.exports = Function.prototype.bind || implementation;

},{"./implementation":5}],7:[function(_dereq_,module,exports){
(function (global){
var topLevel = typeof global !== 'undefined' ? global :
    typeof window !== 'undefined' ? window : {}
var minDoc = _dereq_('min-document');

var doccy;

if (typeof document !== 'undefined') {
    doccy = document;
} else {
    doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];

    if (!doccy) {
        doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
    }
}

module.exports = doccy;

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{"min-document":1}],8:[function(_dereq_,module,exports){
(function (global){
var win;

if (typeof window !== "undefined") {
    win = window;
} else if (typeof global !== "undefined") {
    win = global;
} else if (typeof self !== "undefined"){
    win = self;
} else {
    win = {};
}

module.exports = win;

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{}],9:[function(_dereq_,module,exports){
module.exports = isFunction

var toString = Object.prototype.toString

function isFunction (fn) {
  var string = toString.call(fn)
  return string === '[object Function]' ||
    (typeof fn === 'function' && string !== '[object RegExp]') ||
    (typeof window !== 'undefined' &&
     // IE8 and below
     (fn === window.setTimeout ||
      fn === window.alert ||
      fn === window.confirm ||
      fn === window.prompt))
};

},{}],10:[function(_dereq_,module,exports){
var getNative = _dereq_('../internal/getNative');

/* Native method references for those with the same name as other `lodash` methods. */
var nativeNow = getNative(Date, 'now');

/**
 * Gets the number of milliseconds that have elapsed since the Unix epoch
 * (1 January 1970 00:00:00 UTC).
 *
 * @static
 * @memberOf _
 * @category Date
 * @example
 *
 * _.defer(function(stamp) {
 *   console.log(_.now() - stamp);
 * }, _.now());
 * // => logs the number of milliseconds it took for the deferred function to be invoked
 */
var now = nativeNow || function() {
  return new Date().getTime();
};

module.exports = now;

},{"../internal/getNative":26}],11:[function(_dereq_,module,exports){
var isObject = _dereq_('../lang/isObject'),
    now = _dereq_('../date/now');

/** Used as the `TypeError` message for "Functions" methods. */
var FUNC_ERROR_TEXT = 'Expected a function';

/* Native method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;

/**
 * Creates a debounced function that delays invoking `func` until after `wait`
 * milliseconds have elapsed since the last time the debounced function was
 * invoked. The debounced function comes with a `cancel` method to cancel
 * delayed invocations. Provide an options object to indicate that `func`
 * should be invoked on the leading and/or trailing edge of the `wait` timeout.
 * Subsequent calls to the debounced function return the result of the last
 * `func` invocation.
 *
 * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
 * on the trailing edge of the timeout only if the the debounced function is
 * invoked more than once during the `wait` timeout.
 *
 * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
 * for details over the differences between `_.debounce` and `_.throttle`.
 *
 * @static
 * @memberOf _
 * @category Function
 * @param {Function} func The function to debounce.
 * @param {number} [wait=0] The number of milliseconds to delay.
 * @param {Object} [options] The options object.
 * @param {boolean} [options.leading=false] Specify invoking on the leading
 *  edge of the timeout.
 * @param {number} [options.maxWait] The maximum time `func` is allowed to be
 *  delayed before it's invoked.
 * @param {boolean} [options.trailing=true] Specify invoking on the trailing
 *  edge of the timeout.
 * @returns {Function} Returns the new debounced function.
 * @example
 *
 * // avoid costly calculations while the window size is in flux
 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
 *
 * // invoke `sendMail` when the click event is fired, debouncing subsequent calls
 * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
 *   'leading': true,
 *   'trailing': false
 * }));
 *
 * // ensure `batchLog` is invoked once after 1 second of debounced calls
 * var source = new EventSource('/stream');
 * jQuery(source).on('message', _.debounce(batchLog, 250, {
 *   'maxWait': 1000
 * }));
 *
 * // cancel a debounced call
 * var todoChanges = _.debounce(batchLog, 1000);
 * Object.observe(models.todo, todoChanges);
 *
 * Object.observe(models, function(changes) {
 *   if (_.find(changes, { 'user': 'todo', 'type': 'delete'})) {
 *     todoChanges.cancel();
 *   }
 * }, ['delete']);
 *
 * // ...at some point `models.todo` is changed
 * models.todo.completed = true;
 *
 * // ...before 1 second has passed `models.todo` is deleted
 * // which cancels the debounced `todoChanges` call
 * delete models.todo;
 */
function debounce(func, wait, options) {
  var args,
      maxTimeoutId,
      result,
      stamp,
      thisArg,
      timeoutId,
      trailingCall,
      lastCalled = 0,
      maxWait = false,
      trailing = true;

  if (typeof func != 'function') {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  wait = wait < 0 ? 0 : (+wait || 0);
  if (options === true) {
    var leading = true;
    trailing = false;
  } else if (isObject(options)) {
    leading = !!options.leading;
    maxWait = 'maxWait' in options && nativeMax(+options.maxWait || 0, wait);
    trailing = 'trailing' in options ? !!options.trailing : trailing;
  }

  function cancel() {
    if (timeoutId) {
      clearTimeout(timeoutId);
    }
    if (maxTimeoutId) {
      clearTimeout(maxTimeoutId);
    }
    lastCalled = 0;
    maxTimeoutId = timeoutId = trailingCall = undefined;
  }

  function complete(isCalled, id) {
    if (id) {
      clearTimeout(id);
    }
    maxTimeoutId = timeoutId = trailingCall = undefined;
    if (isCalled) {
      lastCalled = now();
      result = func.apply(thisArg, args);
      if (!timeoutId && !maxTimeoutId) {
        args = thisArg = undefined;
      }
    }
  }

  function delayed() {
    var remaining = wait - (now() - stamp);
    if (remaining <= 0 || remaining > wait) {
      complete(trailingCall, maxTimeoutId);
    } else {
      timeoutId = setTimeout(delayed, remaining);
    }
  }

  function maxDelayed() {
    complete(trailing, timeoutId);
  }

  function debounced() {
    args = arguments;
    stamp = now();
    thisArg = this;
    trailingCall = trailing && (timeoutId || !leading);

    if (maxWait === false) {
      var leadingCall = leading && !timeoutId;
    } else {
      if (!maxTimeoutId && !leading) {
        lastCalled = stamp;
      }
      var remaining = maxWait - (stamp - lastCalled),
          isCalled = remaining <= 0 || remaining > maxWait;

      if (isCalled) {
        if (maxTimeoutId) {
          maxTimeoutId = clearTimeout(maxTimeoutId);
        }
        lastCalled = stamp;
        result = func.apply(thisArg, args);
      }
      else if (!maxTimeoutId) {
        maxTimeoutId = setTimeout(maxDelayed, remaining);
      }
    }
    if (isCalled && timeoutId) {
      timeoutId = clearTimeout(timeoutId);
    }
    else if (!timeoutId && wait !== maxWait) {
      timeoutId = setTimeout(delayed, wait);
    }
    if (leadingCall) {
      isCalled = true;
      result = func.apply(thisArg, args);
    }
    if (isCalled && !timeoutId && !maxTimeoutId) {
      args = thisArg = undefined;
    }
    return result;
  }
  debounced.cancel = cancel;
  return debounced;
}

module.exports = debounce;

},{"../date/now":10,"../lang/isObject":39}],12:[function(_dereq_,module,exports){
/** Used as the `TypeError` message for "Functions" methods. */
var FUNC_ERROR_TEXT = 'Expected a function';

/* Native method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;

/**
 * Creates a function that invokes `func` with the `this` binding of the
 * created function and arguments from `start` and beyond provided as an array.
 *
 * **Note:** This method is based on the [rest parameter](https://developer.mozilla.org/Web/JavaScript/Reference/Functions/rest_parameters).
 *
 * @static
 * @memberOf _
 * @category Function
 * @param {Function} func The function to apply a rest parameter to.
 * @param {number} [start=func.length-1] The start position of the rest parameter.
 * @returns {Function} Returns the new function.
 * @example
 *
 * var say = _.restParam(function(what, names) {
 *   return what + ' ' + _.initial(names).join(', ') +
 *     (_.size(names) > 1 ? ', & ' : '') + _.last(names);
 * });
 *
 * say('hello', 'fred', 'barney', 'pebbles');
 * // => 'hello fred, barney, & pebbles'
 */
function restParam(func, start) {
  if (typeof func != 'function') {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0);
  return function() {
    var args = arguments,
        index = -1,
        length = nativeMax(args.length - start, 0),
        rest = Array(length);

    while (++index < length) {
      rest[index] = args[start + index];
    }
    switch (start) {
      case 0: return func.call(this, rest);
      case 1: return func.call(this, args[0], rest);
      case 2: return func.call(this, args[0], args[1], rest);
    }
    var otherArgs = Array(start + 1);
    index = -1;
    while (++index < start) {
      otherArgs[index] = args[index];
    }
    otherArgs[start] = rest;
    return func.apply(this, otherArgs);
  };
}

module.exports = restParam;

},{}],13:[function(_dereq_,module,exports){
var debounce = _dereq_('./debounce'),
    isObject = _dereq_('../lang/isObject');

/** Used as the `TypeError` message for "Functions" methods. */
var FUNC_ERROR_TEXT = 'Expected a function';

/**
 * Creates a throttled function that only invokes `func` at most once per
 * every `wait` milliseconds. The throttled function comes with a `cancel`
 * method to cancel delayed invocations. Provide an options object to indicate
 * that `func` should be invoked on the leading and/or trailing edge of the
 * `wait` timeout. Subsequent calls to the throttled function return the
 * result of the last `func` call.
 *
 * **Note:** If `leading` and `trailing` options are `true`, `func` is invoked
 * on the trailing edge of the timeout only if the the throttled function is
 * invoked more than once during the `wait` timeout.
 *
 * See [David Corbacho's article](http://drupalmotion.com/article/debounce-and-throttle-visual-explanation)
 * for details over the differences between `_.throttle` and `_.debounce`.
 *
 * @static
 * @memberOf _
 * @category Function
 * @param {Function} func The function to throttle.
 * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
 * @param {Object} [options] The options object.
 * @param {boolean} [options.leading=true] Specify invoking on the leading
 *  edge of the timeout.
 * @param {boolean} [options.trailing=true] Specify invoking on the trailing
 *  edge of the timeout.
 * @returns {Function} Returns the new throttled function.
 * @example
 *
 * // avoid excessively updating the position while scrolling
 * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
 *
 * // invoke `renewToken` when the click event is fired, but not more than once every 5 minutes
 * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
 *   'trailing': false
 * }));
 *
 * // cancel a trailing throttled call
 * jQuery(window).on('popstate', throttled.cancel);
 */
function throttle(func, wait, options) {
  var leading = true,
      trailing = true;

  if (typeof func != 'function') {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  if (options === false) {
    leading = false;
  } else if (isObject(options)) {
    leading = 'leading' in options ? !!options.leading : leading;
    trailing = 'trailing' in options ? !!options.trailing : trailing;
  }
  return debounce(func, wait, { 'leading': leading, 'maxWait': +wait, 'trailing': trailing });
}

module.exports = throttle;

},{"../lang/isObject":39,"./debounce":11}],14:[function(_dereq_,module,exports){
/**
 * Copies the values of `source` to `array`.
 *
 * @private
 * @param {Array} source The array to copy values from.
 * @param {Array} [array=[]] The array to copy values to.
 * @returns {Array} Returns `array`.
 */
function arrayCopy(source, array) {
  var index = -1,
      length = source.length;

  array || (array = Array(length));
  while (++index < length) {
    array[index] = source[index];
  }
  return array;
}

module.exports = arrayCopy;

},{}],15:[function(_dereq_,module,exports){
/**
 * A specialized version of `_.forEach` for arrays without support for callback
 * shorthands and `this` binding.
 *
 * @private
 * @param {Array} array The array to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns `array`.
 */
function arrayEach(array, iteratee) {
  var index = -1,
      length = array.length;

  while (++index < length) {
    if (iteratee(array[index], index, array) === false) {
      break;
    }
  }
  return array;
}

module.exports = arrayEach;

},{}],16:[function(_dereq_,module,exports){
/**
 * Copies properties of `source` to `object`.
 *
 * @private
 * @param {Object} source The object to copy properties from.
 * @param {Array} props The property names to copy.
 * @param {Object} [object={}] The object to copy properties to.
 * @returns {Object} Returns `object`.
 */
function baseCopy(source, props, object) {
  object || (object = {});

  var index = -1,
      length = props.length;

  while (++index < length) {
    var key = props[index];
    object[key] = source[key];
  }
  return object;
}

module.exports = baseCopy;

},{}],17:[function(_dereq_,module,exports){
var createBaseFor = _dereq_('./createBaseFor');

/**
 * The base implementation of `baseForIn` and `baseForOwn` which iterates
 * over `object` properties returned by `keysFunc` invoking `iteratee` for
 * each property. Iteratee functions may exit iteration early by explicitly
 * returning `false`.
 *
 * @private
 * @param {Object} object The object to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @param {Function} keysFunc The function to get the keys of `object`.
 * @returns {Object} Returns `object`.
 */
var baseFor = createBaseFor();

module.exports = baseFor;

},{"./createBaseFor":24}],18:[function(_dereq_,module,exports){
var baseFor = _dereq_('./baseFor'),
    keysIn = _dereq_('../object/keysIn');

/**
 * The base implementation of `_.forIn` without support for callback
 * shorthands and `this` binding.
 *
 * @private
 * @param {Object} object The object to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Object} Returns `object`.
 */
function baseForIn(object, iteratee) {
  return baseFor(object, iteratee, keysIn);
}

module.exports = baseForIn;

},{"../object/keysIn":45,"./baseFor":17}],19:[function(_dereq_,module,exports){
var arrayEach = _dereq_('./arrayEach'),
    baseMergeDeep = _dereq_('./baseMergeDeep'),
    isArray = _dereq_('../lang/isArray'),
    isArrayLike = _dereq_('./isArrayLike'),
    isObject = _dereq_('../lang/isObject'),
    isObjectLike = _dereq_('./isObjectLike'),
    isTypedArray = _dereq_('../lang/isTypedArray'),
    keys = _dereq_('../object/keys');

/**
 * The base implementation of `_.merge` without support for argument juggling,
 * multiple sources, and `this` binding `customizer` functions.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @param {Function} [customizer] The function to customize merged values.
 * @param {Array} [stackA=[]] Tracks traversed source objects.
 * @param {Array} [stackB=[]] Associates values with source counterparts.
 * @returns {Object} Returns `object`.
 */
function baseMerge(object, source, customizer, stackA, stackB) {
  if (!isObject(object)) {
    return object;
  }
  var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source)),
      props = isSrcArr ? undefined : keys(source);

  arrayEach(props || source, function(srcValue, key) {
    if (props) {
      key = srcValue;
      srcValue = source[key];
    }
    if (isObjectLike(srcValue)) {
      stackA || (stackA = []);
      stackB || (stackB = []);
      baseMergeDeep(object, source, key, baseMerge, customizer, stackA, stackB);
    }
    else {
      var value = object[key],
          result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
          isCommon = result === undefined;

      if (isCommon) {
        result = srcValue;
      }
      if ((result !== undefined || (isSrcArr && !(key in object))) &&
          (isCommon || (result === result ? (result !== value) : (value === value)))) {
        object[key] = result;
      }
    }
  });
  return object;
}

module.exports = baseMerge;

},{"../lang/isArray":36,"../lang/isObject":39,"../lang/isTypedArray":42,"../object/keys":44,"./arrayEach":15,"./baseMergeDeep":20,"./isArrayLike":27,"./isObjectLike":32}],20:[function(_dereq_,module,exports){
var arrayCopy = _dereq_('./arrayCopy'),
    isArguments = _dereq_('../lang/isArguments'),
    isArray = _dereq_('../lang/isArray'),
    isArrayLike = _dereq_('./isArrayLike'),
    isPlainObject = _dereq_('../lang/isPlainObject'),
    isTypedArray = _dereq_('../lang/isTypedArray'),
    toPlainObject = _dereq_('../lang/toPlainObject');

/**
 * A specialized version of `baseMerge` for arrays and objects which performs
 * deep merges and tracks traversed objects enabling objects with circular
 * references to be merged.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @param {string} key The key of the value to merge.
 * @param {Function} mergeFunc The function to merge values.
 * @param {Function} [customizer] The function to customize merged values.
 * @param {Array} [stackA=[]] Tracks traversed source objects.
 * @param {Array} [stackB=[]] Associates values with source counterparts.
 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
 */
function baseMergeDeep(object, source, key, mergeFunc, customizer, stackA, stackB) {
  var length = stackA.length,
      srcValue = source[key];

  while (length--) {
    if (stackA[length] == srcValue) {
      object[key] = stackB[length];
      return;
    }
  }
  var value = object[key],
      result = customizer ? customizer(value, srcValue, key, object, source) : undefined,
      isCommon = result === undefined;

  if (isCommon) {
    result = srcValue;
    if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) {
      result = isArray(value)
        ? value
        : (isArrayLike(value) ? arrayCopy(value) : []);
    }
    else if (isPlainObject(srcValue) || isArguments(srcValue)) {
      result = isArguments(value)
        ? toPlainObject(value)
        : (isPlainObject(value) ? value : {});
    }
    else {
      isCommon = false;
    }
  }
  // Add the source value to the stack of traversed objects and associate
  // it with its merged value.
  stackA.push(srcValue);
  stackB.push(result);

  if (isCommon) {
    // Recursively merge objects and arrays (susceptible to call stack limits).
    object[key] = mergeFunc(result, srcValue, customizer, stackA, stackB);
  } else if (result === result ? (result !== value) : (value === value)) {
    object[key] = result;
  }
}

module.exports = baseMergeDeep;

},{"../lang/isArguments":35,"../lang/isArray":36,"../lang/isPlainObject":40,"../lang/isTypedArray":42,"../lang/toPlainObject":43,"./arrayCopy":14,"./isArrayLike":27}],21:[function(_dereq_,module,exports){
var toObject = _dereq_('./toObject');

/**
 * The base implementation of `_.property` without support for deep paths.
 *
 * @private
 * @param {string} key The key of the property to get.
 * @returns {Function} Returns the new function.
 */
function baseProperty(key) {
  return function(object) {
    return object == null ? undefined : toObject(object)[key];
  };
}

module.exports = baseProperty;

},{"./toObject":34}],22:[function(_dereq_,module,exports){
var identity = _dereq_('../utility/identity');

/**
 * A specialized version of `baseCallback` which only supports `this` binding
 * and specifying the number of arguments to provide to `func`.
 *
 * @private
 * @param {Function} func The function to bind.
 * @param {*} thisArg The `this` binding of `func`.
 * @param {number} [argCount] The number of arguments to provide to `func`.
 * @returns {Function} Returns the callback.
 */
function bindCallback(func, thisArg, argCount) {
  if (typeof func != 'function') {
    return identity;
  }
  if (thisArg === undefined) {
    return func;
  }
  switch (argCount) {
    case 1: return function(value) {
      return func.call(thisArg, value);
    };
    case 3: return function(value, index, collection) {
      return func.call(thisArg, value, index, collection);
    };
    case 4: return function(accumulator, value, index, collection) {
      return func.call(thisArg, accumulator, value, index, collection);
    };
    case 5: return function(value, other, key, object, source) {
      return func.call(thisArg, value, other, key, object, source);
    };
  }
  return function() {
    return func.apply(thisArg, arguments);
  };
}

module.exports = bindCallback;

},{"../utility/identity":48}],23:[function(_dereq_,module,exports){
var bindCallback = _dereq_('./bindCallback'),
    isIterateeCall = _dereq_('./isIterateeCall'),
    restParam = _dereq_('../function/restParam');

/**
 * Creates a `_.assign`, `_.defaults`, or `_.merge` function.
 *
 * @private
 * @param {Function} assigner The function to assign values.
 * @returns {Function} Returns the new assigner function.
 */
function createAssigner(assigner) {
  return restParam(function(object, sources) {
    var index = -1,
        length = object == null ? 0 : sources.length,
        customizer = length > 2 ? sources[length - 2] : undefined,
        guard = length > 2 ? sources[2] : undefined,
        thisArg = length > 1 ? sources[length - 1] : undefined;

    if (typeof customizer == 'function') {
      customizer = bindCallback(customizer, thisArg, 5);
      length -= 2;
    } else {
      customizer = typeof thisArg == 'function' ? thisArg : undefined;
      length -= (customizer ? 1 : 0);
    }
    if (guard && isIterateeCall(sources[0], sources[1], guard)) {
      customizer = length < 3 ? undefined : customizer;
      length = 1;
    }
    while (++index < length) {
      var source = sources[index];
      if (source) {
        assigner(object, source, customizer);
      }
    }
    return object;
  });
}

module.exports = createAssigner;

},{"../function/restParam":12,"./bindCallback":22,"./isIterateeCall":30}],24:[function(_dereq_,module,exports){
var toObject = _dereq_('./toObject');

/**
 * Creates a base function for `_.forIn` or `_.forInRight`.
 *
 * @private
 * @param {boolean} [fromRight] Specify iterating from right to left.
 * @returns {Function} Returns the new base function.
 */
function createBaseFor(fromRight) {
  return function(object, iteratee, keysFunc) {
    var iterable = toObject(object),
        props = keysFunc(object),
        length = props.length,
        index = fromRight ? length : -1;

    while ((fromRight ? index-- : ++index < length)) {
      var key = props[index];
      if (iteratee(iterable[key], key, iterable) === false) {
        break;
      }
    }
    return object;
  };
}

module.exports = createBaseFor;

},{"./toObject":34}],25:[function(_dereq_,module,exports){
var baseProperty = _dereq_('./baseProperty');

/**
 * Gets the "length" property value of `object`.
 *
 * **Note:** This function is used to avoid a [JIT bug](https://bugs.webkit.org/show_bug.cgi?id=142792)
 * that affects Safari on at least iOS 8.1-8.3 ARM64.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {*} Returns the "length" value.
 */
var getLength = baseProperty('length');

module.exports = getLength;

},{"./baseProperty":21}],26:[function(_dereq_,module,exports){
var isNative = _dereq_('../lang/isNative');

/**
 * Gets the native function at `key` of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {string} key The key of the method to get.
 * @returns {*} Returns the function if it's native, else `undefined`.
 */
function getNative(object, key) {
  var value = object == null ? undefined : object[key];
  return isNative(value) ? value : undefined;
}

module.exports = getNative;

},{"../lang/isNative":38}],27:[function(_dereq_,module,exports){
var getLength = _dereq_('./getLength'),
    isLength = _dereq_('./isLength');

/**
 * Checks if `value` is array-like.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
 */
function isArrayLike(value) {
  return value != null && isLength(getLength(value));
}

module.exports = isArrayLike;

},{"./getLength":25,"./isLength":31}],28:[function(_dereq_,module,exports){
/**
 * Checks if `value` is a host object in IE < 9.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
 */
var isHostObject = (function() {
  try {
    Object({ 'toString': 0 } + '');
  } catch(e) {
    return function() { return false; };
  }
  return function(value) {
    // IE < 9 presents many host objects as `Object` objects that can coerce
    // to strings despite having improperly defined `toString` methods.
    return typeof value.toString != 'function' && typeof (value + '') == 'string';
  };
}());

module.exports = isHostObject;

},{}],29:[function(_dereq_,module,exports){
/** Used to detect unsigned integer values. */
var reIsUint = /^\d+$/;

/**
 * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer)
 * of an array-like value.
 */
var MAX_SAFE_INTEGER = 9007199254740991;

/**
 * Checks if `value` is a valid array-like index.
 *
 * @private
 * @param {*} value The value to check.
 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
 */
function isIndex(value, length) {
  value = (typeof value == 'number' || reIsUint.test(value)) ? +value : -1;
  length = length == null ? MAX_SAFE_INTEGER : length;
  return value > -1 && value % 1 == 0 && value < length;
}

module.exports = isIndex;

},{}],30:[function(_dereq_,module,exports){
var isArrayLike = _dereq_('./isArrayLike'),
    isIndex = _dereq_('./isIndex'),
    isObject = _dereq_('../lang/isObject');

/**
 * Checks if the provided arguments are from an iteratee call.
 *
 * @private
 * @param {*} value The potential iteratee value argument.
 * @param {*} index The potential iteratee index or key argument.
 * @param {*} object The potential iteratee object argument.
 * @returns {boolean} Returns `true` if the arguments are from an iteratee call, else `false`.
 */
function isIterateeCall(value, index, object) {
  if (!isObject(object)) {
    return false;
  }
  var type = typeof index;
  if (type == 'number'
      ? (isArrayLike(object) && isIndex(index, object.length))
      : (type == 'string' && index in object)) {
    var other = object[index];
    return value === value ? (value === other) : (other !== other);
  }
  return false;
}

module.exports = isIterateeCall;

},{"../lang/isObject":39,"./isArrayLike":27,"./isIndex":29}],31:[function(_dereq_,module,exports){
/**
 * Used as the [maximum length](http://ecma-international.org/ecma-262/6.0/#sec-number.max_safe_integer)
 * of an array-like value.
 */
var MAX_SAFE_INTEGER = 9007199254740991;

/**
 * Checks if `value` is a valid array-like length.
 *
 * **Note:** This function is based on [`ToLength`](http://ecma-international.org/ecma-262/6.0/#sec-tolength).
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
 */
function isLength(value) {
  return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
}

module.exports = isLength;

},{}],32:[function(_dereq_,module,exports){
/**
 * Checks if `value` is object-like.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
 */
function isObjectLike(value) {
  return !!value && typeof value == 'object';
}

module.exports = isObjectLike;

},{}],33:[function(_dereq_,module,exports){
var isArguments = _dereq_('../lang/isArguments'),
    isArray = _dereq_('../lang/isArray'),
    isIndex = _dereq_('./isIndex'),
    isLength = _dereq_('./isLength'),
    isString = _dereq_('../lang/isString'),
    keysIn = _dereq_('../object/keysIn');

/** Used for native method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * A fallback implementation of `Object.keys` which creates an array of the
 * own enumerable property names of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 */
function shimKeys(object) {
  var props = keysIn(object),
      propsLength = props.length,
      length = propsLength && object.length;

  var allowIndexes = !!length && isLength(length) &&
    (isArray(object) || isArguments(object) || isString(object));

  var index = -1,
      result = [];

  while (++index < propsLength) {
    var key = props[index];
    if ((allowIndexes && isIndex(key, length)) || hasOwnProperty.call(object, key)) {
      result.push(key);
    }
  }
  return result;
}

module.exports = shimKeys;

},{"../lang/isArguments":35,"../lang/isArray":36,"../lang/isString":41,"../object/keysIn":45,"./isIndex":29,"./isLength":31}],34:[function(_dereq_,module,exports){
var isObject = _dereq_('../lang/isObject'),
    isString = _dereq_('../lang/isString'),
    support = _dereq_('../support');

/**
 * Converts `value` to an object if it's not one.
 *
 * @private
 * @param {*} value The value to process.
 * @returns {Object} Returns the object.
 */
function toObject(value) {
  if (support.unindexedChars && isString(value)) {
    var index = -1,
        length = value.length,
        result = Object(value);

    while (++index < length) {
      result[index] = value.charAt(index);
    }
    return result;
  }
  return isObject(value) ? value : Object(value);
}

module.exports = toObject;

},{"../lang/isObject":39,"../lang/isString":41,"../support":47}],35:[function(_dereq_,module,exports){
var isArrayLike = _dereq_('../internal/isArrayLike'),
    isObjectLike = _dereq_('../internal/isObjectLike');

/** Used for native method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/** Native method references. */
var propertyIsEnumerable = objectProto.propertyIsEnumerable;

/**
 * Checks if `value` is classified as an `arguments` object.
 *
 * @static
 * @memberOf _
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
 * @example
 *
 * _.isArguments(function() { return arguments; }());
 * // => true
 *
 * _.isArguments([1, 2, 3]);
 * // => false
 */
function isArguments(value) {
  return isObjectLike(value) && isArrayLike(value) &&
    hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');
}

module.exports = isArguments;

},{"../internal/isArrayLike":27,"../internal/isObjectLike":32}],36:[function(_dereq_,module,exports){
var getNative = _dereq_('../internal/getNative'),
    isLength = _dereq_('../internal/isLength'),
    isObjectLike = _dereq_('../internal/isObjectLike');

/** `Object#toString` result references. */
var arrayTag = '[object Array]';

/** Used for native method references. */
var objectProto = Object.prototype;

/**
 * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
 * of values.
 */
var objToString = objectProto.toString;

/* Native method references for those with the same name as other `lodash` methods. */
var nativeIsArray = getNative(Array, 'isArray');

/**
 * Checks if `value` is classified as an `Array` object.
 *
 * @static
 * @memberOf _
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
 * @example
 *
 * _.isArray([1, 2, 3]);
 * // => true
 *
 * _.isArray(function() { return arguments; }());
 * // => false
 */
var isArray = nativeIsArray || function(value) {
  return isObjectLike(value) && isLength(value.length) && objToString.call(value) == arrayTag;
};

module.exports = isArray;

},{"../internal/getNative":26,"../internal/isLength":31,"../internal/isObjectLike":32}],37:[function(_dereq_,module,exports){
var isObject = _dereq_('./isObject');

/** `Object#toString` result references. */
var funcTag = '[object Function]';

/** Used for native method references. */
var objectProto = Object.prototype;

/**
 * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
 * of values.
 */
var objToString = objectProto.toString;

/**
 * Checks if `value` is classified as a `Function` object.
 *
 * @static
 * @memberOf _
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
 * @example
 *
 * _.isFunction(_);
 * // => true
 *
 * _.isFunction(/abc/);
 * // => false
 */
function isFunction(value) {
  // The use of `Object#toString` avoids issues with the `typeof` operator
  // in older versions of Chrome and Safari which return 'function' for regexes
  // and Safari 8 which returns 'object' for typed array constructors.
  return isObject(value) && objToString.call(value) == funcTag;
}

module.exports = isFunction;

},{"./isObject":39}],38:[function(_dereq_,module,exports){
var isFunction = _dereq_('./isFunction'),
    isHostObject = _dereq_('../internal/isHostObject'),
    isObjectLike = _dereq_('../internal/isObjectLike');

/** Used to detect host constructors (Safari > 5). */
var reIsHostCtor = /^\[object .+?Constructor\]$/;

/** Used for native method references. */
var objectProto = Object.prototype;

/** Used to resolve the decompiled source of functions. */
var fnToString = Function.prototype.toString;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/** Used to detect if a method is native. */
var reIsNative = RegExp('^' +
  fnToString.call(hasOwnProperty).replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')
  .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
);

/**
 * Checks if `value` is a native function.
 *
 * @static
 * @memberOf _
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a native function, else `false`.
 * @example
 *
 * _.isNative(Array.prototype.push);
 * // => true
 *
 * _.isNative(_);
 * // => false
 */
function isNative(value) {
  if (value == null) {
    return false;
  }
  if (isFunction(value)) {
    return reIsNative.test(fnToString.call(value));
  }
  return isObjectLike(value) && (isHostObject(value) ? reIsNative : reIsHostCtor).test(value);
}

module.exports = isNative;

},{"../internal/isHostObject":28,"../internal/isObjectLike":32,"./isFunction":37}],39:[function(_dereq_,module,exports){
/**
 * Checks if `value` is the [language type](https://es5.github.io/#x8) of `Object`.
 * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
 *
 * @static
 * @memberOf _
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
 * @example
 *
 * _.isObject({});
 * // => true
 *
 * _.isObject([1, 2, 3]);
 * // => true
 *
 * _.isObject(1);
 * // => false
 */
function isObject(value) {
  // Avoid a V8 JIT bug in Chrome 19-20.
  // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
  var type = typeof value;
  return !!value && (type == 'object' || type == 'function');
}

module.exports = isObject;

},{}],40:[function(_dereq_,module,exports){
var baseForIn = _dereq_('../internal/baseForIn'),
    isArguments = _dereq_('./isArguments'),
    isHostObject = _dereq_('../internal/isHostObject'),
    isObjectLike = _dereq_('../internal/isObjectLike'),
    support = _dereq_('../support');

/** `Object#toString` result references. */
var objectTag = '[object Object]';

/** Used for native method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
 * of values.
 */
var objToString = objectProto.toString;

/**
 * Checks if `value` is a plain object, that is, an object created by the
 * `Object` constructor or one with a `[[Prototype]]` of `null`.
 *
 * **Note:** This method assumes objects created by the `Object` constructor
 * have no inherited enumerable properties.
 *
 * @static
 * @memberOf _
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 * }
 *
 * _.isPlainObject(new Foo);
 * // => false
 *
 * _.isPlainObject([1, 2, 3]);
 * // => false
 *
 * _.isPlainObject({ 'x': 0, 'y': 0 });
 * // => true
 *
 * _.isPlainObject(Object.create(null));
 * // => true
 */
function isPlainObject(value) {
  var Ctor;

  // Exit early for non `Object` objects.
  if (!(isObjectLike(value) && objToString.call(value) == objectTag && !isHostObject(value) && !isArguments(value)) ||
      (!hasOwnProperty.call(value, 'constructor') && (Ctor = value.constructor, typeof Ctor == 'function' && !(Ctor instanceof Ctor)))) {
    return false;
  }
  // IE < 9 iterates inherited properties before own properties. If the first
  // iterated property is an object's own property then there are no inherited
  // enumerable properties.
  var result;
  if (support.ownLast) {
    baseForIn(value, function(subValue, key, object) {
      result = hasOwnProperty.call(object, key);
      return false;
    });
    return result !== false;
  }
  // In most environments an object's own properties are iterated before
  // its inherited properties. If the last iterated property is an object's
  // own property then there are no inherited enumerable properties.
  baseForIn(value, function(subValue, key) {
    result = key;
  });
  return result === undefined || hasOwnProperty.call(value, result);
}

module.exports = isPlainObject;

},{"../internal/baseForIn":18,"../internal/isHostObject":28,"../internal/isObjectLike":32,"../support":47,"./isArguments":35}],41:[function(_dereq_,module,exports){
var isObjectLike = _dereq_('../internal/isObjectLike');

/** `Object#toString` result references. */
var stringTag = '[object String]';

/** Used for native method references. */
var objectProto = Object.prototype;

/**
 * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
 * of values.
 */
var objToString = objectProto.toString;

/**
 * Checks if `value` is classified as a `String` primitive or object.
 *
 * @static
 * @memberOf _
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
 * @example
 *
 * _.isString('abc');
 * // => true
 *
 * _.isString(1);
 * // => false
 */
function isString(value) {
  return typeof value == 'string' || (isObjectLike(value) && objToString.call(value) == stringTag);
}

module.exports = isString;

},{"../internal/isObjectLike":32}],42:[function(_dereq_,module,exports){
var isLength = _dereq_('../internal/isLength'),
    isObjectLike = _dereq_('../internal/isObjectLike');

/** `Object#toString` result references. */
var argsTag = '[object Arguments]',
    arrayTag = '[object Array]',
    boolTag = '[object Boolean]',
    dateTag = '[object Date]',
    errorTag = '[object Error]',
    funcTag = '[object Function]',
    mapTag = '[object Map]',
    numberTag = '[object Number]',
    objectTag = '[object Object]',
    regexpTag = '[object RegExp]',
    setTag = '[object Set]',
    stringTag = '[object String]',
    weakMapTag = '[object WeakMap]';

var arrayBufferTag = '[object ArrayBuffer]',
    float32Tag = '[object Float32Array]',
    float64Tag = '[object Float64Array]',
    int8Tag = '[object Int8Array]',
    int16Tag = '[object Int16Array]',
    int32Tag = '[object Int32Array]',
    uint8Tag = '[object Uint8Array]',
    uint8ClampedTag = '[object Uint8ClampedArray]',
    uint16Tag = '[object Uint16Array]',
    uint32Tag = '[object Uint32Array]';

/** Used to identify `toStringTag` values of typed arrays. */
var typedArrayTags = {};
typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
typedArrayTags[uint32Tag] = true;
typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
typedArrayTags[dateTag] = typedArrayTags[errorTag] =
typedArrayTags[funcTag] = typedArrayTags[mapTag] =
typedArrayTags[numberTag] = typedArrayTags[objectTag] =
typedArrayTags[regexpTag] = typedArrayTags[setTag] =
typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;

/** Used for native method references. */
var objectProto = Object.prototype;

/**
 * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
 * of values.
 */
var objToString = objectProto.toString;

/**
 * Checks if `value` is classified as a typed array.
 *
 * @static
 * @memberOf _
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
 * @example
 *
 * _.isTypedArray(new Uint8Array);
 * // => true
 *
 * _.isTypedArray([]);
 * // => false
 */
function isTypedArray(value) {
  return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[objToString.call(value)];
}

module.exports = isTypedArray;

},{"../internal/isLength":31,"../internal/isObjectLike":32}],43:[function(_dereq_,module,exports){
var baseCopy = _dereq_('../internal/baseCopy'),
    keysIn = _dereq_('../object/keysIn');

/**
 * Converts `value` to a plain object flattening inherited enumerable
 * properties of `value` to own properties of the plain object.
 *
 * @static
 * @memberOf _
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {Object} Returns the converted plain object.
 * @example
 *
 * function Foo() {
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.assign({ 'a': 1 }, new Foo);
 * // => { 'a': 1, 'b': 2 }
 *
 * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
 * // => { 'a': 1, 'b': 2, 'c': 3 }
 */
function toPlainObject(value) {
  return baseCopy(value, keysIn(value));
}

module.exports = toPlainObject;

},{"../internal/baseCopy":16,"../object/keysIn":45}],44:[function(_dereq_,module,exports){
var getNative = _dereq_('../internal/getNative'),
    isArrayLike = _dereq_('../internal/isArrayLike'),
    isObject = _dereq_('../lang/isObject'),
    shimKeys = _dereq_('../internal/shimKeys'),
    support = _dereq_('../support');

/* Native method references for those with the same name as other `lodash` methods. */
var nativeKeys = getNative(Object, 'keys');

/**
 * Creates an array of the own enumerable property names of `object`.
 *
 * **Note:** Non-object values are coerced to objects. See the
 * [ES spec](http://ecma-international.org/ecma-262/6.0/#sec-object.keys)
 * for more details.
 *
 * @static
 * @memberOf _
 * @category Object
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.keys(new Foo);
 * // => ['a', 'b'] (iteration order is not guaranteed)
 *
 * _.keys('hi');
 * // => ['0', '1']
 */
var keys = !nativeKeys ? shimKeys : function(object) {
  var Ctor = object == null ? undefined : object.constructor;
  if ((typeof Ctor == 'function' && Ctor.prototype === object) ||
      (typeof object == 'function' ? support.enumPrototypes : isArrayLike(object))) {
    return shimKeys(object);
  }
  return isObject(object) ? nativeKeys(object) : [];
};

module.exports = keys;

},{"../internal/getNative":26,"../internal/isArrayLike":27,"../internal/shimKeys":33,"../lang/isObject":39,"../support":47}],45:[function(_dereq_,module,exports){
var arrayEach = _dereq_('../internal/arrayEach'),
    isArguments = _dereq_('../lang/isArguments'),
    isArray = _dereq_('../lang/isArray'),
    isFunction = _dereq_('../lang/isFunction'),
    isIndex = _dereq_('../internal/isIndex'),
    isLength = _dereq_('../internal/isLength'),
    isObject = _dereq_('../lang/isObject'),
    isString = _dereq_('../lang/isString'),
    support = _dereq_('../support');

/** `Object#toString` result references. */
var arrayTag = '[object Array]',
    boolTag = '[object Boolean]',
    dateTag = '[object Date]',
    errorTag = '[object Error]',
    funcTag = '[object Function]',
    numberTag = '[object Number]',
    objectTag = '[object Object]',
    regexpTag = '[object RegExp]',
    stringTag = '[object String]';

/** Used to fix the JScript `[[DontEnum]]` bug. */
var shadowProps = [
  'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
  'toLocaleString', 'toString', 'valueOf'
];

/** Used for native method references. */
var errorProto = Error.prototype,
    objectProto = Object.prototype,
    stringProto = String.prototype;

/** Used to check objects for own properties. */
var hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
 * of values.
 */
var objToString = objectProto.toString;

/** Used to avoid iterating over non-enumerable properties in IE < 9. */
var nonEnumProps = {};
nonEnumProps[arrayTag] = nonEnumProps[dateTag] = nonEnumProps[numberTag] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
nonEnumProps[boolTag] = nonEnumProps[stringTag] = { 'constructor': true, 'toString': true, 'valueOf': true };
nonEnumProps[errorTag] = nonEnumProps[funcTag] = nonEnumProps[regexpTag] = { 'constructor': true, 'toString': true };
nonEnumProps[objectTag] = { 'constructor': true };

arrayEach(shadowProps, function(key) {
  for (var tag in nonEnumProps) {
    if (hasOwnProperty.call(nonEnumProps, tag)) {
      var props = nonEnumProps[tag];
      props[key] = hasOwnProperty.call(props, key);
    }
  }
});

/**
 * Creates an array of the own and inherited enumerable property names of `object`.
 *
 * **Note:** Non-object values are coerced to objects.
 *
 * @static
 * @memberOf _
 * @category Object
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.keysIn(new Foo);
 * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
 */
function keysIn(object) {
  if (object == null) {
    return [];
  }
  if (!isObject(object)) {
    object = Object(object);
  }
  var length = object.length;

  length = (length && isLength(length) &&
    (isArray(object) || isArguments(object) || isString(object)) && length) || 0;

  var Ctor = object.constructor,
      index = -1,
      proto = (isFunction(Ctor) && Ctor.prototype) || objectProto,
      isProto = proto === object,
      result = Array(length),
      skipIndexes = length > 0,
      skipErrorProps = support.enumErrorProps && (object === errorProto || object instanceof Error),
      skipProto = support.enumPrototypes && isFunction(object);

  while (++index < length) {
    result[index] = (index + '');
  }
  // lodash skips the `constructor` property when it infers it's iterating
  // over a `prototype` object because IE < 9 can't set the `[[Enumerable]]`
  // attribute of an existing property and the `constructor` property of a
  // prototype defaults to non-enumerable.
  for (var key in object) {
    if (!(skipProto && key == 'prototype') &&
        !(skipErrorProps && (key == 'message' || key == 'name')) &&
        !(skipIndexes && isIndex(key, length)) &&
        !(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
      result.push(key);
    }
  }
  if (support.nonEnumShadows && object !== objectProto) {
    var tag = object === stringProto ? stringTag : (object === errorProto ? errorTag : objToString.call(object)),
        nonEnums = nonEnumProps[tag] || nonEnumProps[objectTag];

    if (tag == objectTag) {
      proto = objectProto;
    }
    length = shadowProps.length;
    while (length--) {
      key = shadowProps[length];
      var nonEnum = nonEnums[key];
      if (!(isProto && nonEnum) &&
          (nonEnum ? hasOwnProperty.call(object, key) : object[key] !== proto[key])) {
        result.push(key);
      }
    }
  }
  return result;
}

module.exports = keysIn;

},{"../internal/arrayEach":15,"../internal/isIndex":29,"../internal/isLength":31,"../lang/isArguments":35,"../lang/isArray":36,"../lang/isFunction":37,"../lang/isObject":39,"../lang/isString":41,"../support":47}],46:[function(_dereq_,module,exports){
var baseMerge = _dereq_('../internal/baseMerge'),
    createAssigner = _dereq_('../internal/createAssigner');

/**
 * Recursively merges own enumerable properties of the source object(s), that
 * don't resolve to `undefined` into the destination object. Subsequent sources
 * overwrite property assignments of previous sources. If `customizer` is
 * provided it's invoked to produce the merged values of the destination and
 * source properties. If `customizer` returns `undefined` merging is handled
 * by the method instead. The `customizer` is bound to `thisArg` and invoked
 * with five arguments: (objectValue, sourceValue, key, object, source).
 *
 * @static
 * @memberOf _
 * @category Object
 * @param {Object} object The destination object.
 * @param {...Object} [sources] The source objects.
 * @param {Function} [customizer] The function to customize assigned values.
 * @param {*} [thisArg] The `this` binding of `customizer`.
 * @returns {Object} Returns `object`.
 * @example
 *
 * var users = {
 *   'data': [{ 'user': 'barney' }, { 'user': 'fred' }]
 * };
 *
 * var ages = {
 *   'data': [{ 'age': 36 }, { 'age': 40 }]
 * };
 *
 * _.merge(users, ages);
 * // => { 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }
 *
 * // using a customizer callback
 * var object = {
 *   'fruits': ['apple'],
 *   'vegetables': ['beet']
 * };
 *
 * var other = {
 *   'fruits': ['banana'],
 *   'vegetables': ['carrot']
 * };
 *
 * _.merge(object, other, function(a, b) {
 *   if (_.isArray(a)) {
 *     return a.concat(b);
 *   }
 * });
 * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] }
 */
var merge = createAssigner(baseMerge);

module.exports = merge;

},{"../internal/baseMerge":19,"../internal/createAssigner":23}],47:[function(_dereq_,module,exports){
/** Used for native method references. */
var arrayProto = Array.prototype,
    errorProto = Error.prototype,
    objectProto = Object.prototype;

/** Native method references. */
var propertyIsEnumerable = objectProto.propertyIsEnumerable,
    splice = arrayProto.splice;

/**
 * An object environment feature flags.
 *
 * @static
 * @memberOf _
 * @type Object
 */
var support = {};

(function(x) {
  var Ctor = function() { this.x = x; },
      object = { '0': x, 'length': x },
      props = [];

  Ctor.prototype = { 'valueOf': x, 'y': x };
  for (var key in new Ctor) { props.push(key); }

  /**
   * Detect if `name` or `message` properties of `Error.prototype` are
   * enumerable by default (IE < 9, Safari < 5.1).
   *
   * @memberOf _.support
   * @type boolean
   */
  support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') ||
    propertyIsEnumerable.call(errorProto, 'name');

  /**
   * Detect if `prototype` properties are enumerable by default.
   *
   * Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
   * (if the prototype or a property on the prototype has been set)
   * incorrectly set the `[[Enumerable]]` value of a function's `prototype`
   * property to `true`.
   *
   * @memberOf _.support
   * @type boolean
   */
  support.enumPrototypes = propertyIsEnumerable.call(Ctor, 'prototype');

  /**
   * Detect if properties shadowing those on `Object.prototype` are non-enumerable.
   *
   * In IE < 9 an object's own properties, shadowing non-enumerable ones,
   * are made non-enumerable as well (a.k.a the JScript `[[DontEnum]]` bug).
   *
   * @memberOf _.support
   * @type boolean
   */
  support.nonEnumShadows = !/valueOf/.test(props);

  /**
   * Detect if own properties are iterated after inherited properties (IE < 9).
   *
   * @memberOf _.support
   * @type boolean
   */
  support.ownLast = props[0] != 'x';

  /**
   * Detect if `Array#shift` and `Array#splice` augment array-like objects
   * correctly.
   *
   * Firefox < 10, compatibility modes of IE 8, and IE < 9 have buggy Array
   * `shift()` and `splice()` functions that fail to remove the last element,
   * `value[0]`, of array-like objects even though the "length" property is
   * set to `0`. The `shift()` method is buggy in compatibility modes of IE 8,
   * while `splice()` is buggy regardless of mode in IE < 9.
   *
   * @memberOf _.support
   * @type boolean
   */
  support.spliceObjects = (splice.call(object, 0, 1), !object[0]);

  /**
   * Detect lack of support for accessing string characters by index.
   *
   * IE < 8 can't access characters by index. IE 8 can only access characters
   * by index on string literals, not string objects.
   *
   * @memberOf _.support
   * @type boolean
   */
  support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
}(1, 0));

module.exports = support;

},{}],48:[function(_dereq_,module,exports){
/**
 * This method returns the first argument provided to it.
 *
 * @static
 * @memberOf _
 * @category Utility
 * @param {*} value Any value.
 * @returns {*} Returns `value`.
 * @example
 *
 * var object = { 'user': 'fred' };
 *
 * _.identity(object) === object;
 * // => true
 */
function identity(value) {
  return value;
}

module.exports = identity;

},{}],49:[function(_dereq_,module,exports){
'use strict';

// modified from https://github.com/es-shims/es5-shim
var has = Object.prototype.hasOwnProperty;
var toStr = Object.prototype.toString;
var slice = Array.prototype.slice;
var isArgs = _dereq_('./isArguments');
var isEnumerable = Object.prototype.propertyIsEnumerable;
var hasDontEnumBug = !isEnumerable.call({ toString: null }, 'toString');
var hasProtoEnumBug = isEnumerable.call(function () {}, 'prototype');
var dontEnums = [
	'toString',
	'toLocaleString',
	'valueOf',
	'hasOwnProperty',
	'isPrototypeOf',
	'propertyIsEnumerable',
	'constructor'
];
var equalsConstructorPrototype = function (o) {
	var ctor = o.constructor;
	return ctor && ctor.prototype === o;
};
var excludedKeys = {
	$console: true,
	$external: true,
	$frame: true,
	$frameElement: true,
	$frames: true,
	$innerHeight: true,
	$innerWidth: true,
	$outerHeight: true,
	$outerWidth: true,
	$pageXOffset: true,
	$pageYOffset: true,
	$parent: true,
	$scrollLeft: true,
	$scrollTop: true,
	$scrollX: true,
	$scrollY: true,
	$self: true,
	$webkitIndexedDB: true,
	$webkitStorageInfo: true,
	$window: true
};
var hasAutomationEqualityBug = (function () {
	/* global window */
	if (typeof window === 'undefined') { return false; }
	for (var k in window) {
		try {
			if (!excludedKeys['$' + k] && has.call(window, k) && window[k] !== null && typeof window[k] === 'object') {
				try {
					equalsConstructorPrototype(window[k]);
				} catch (e) {
					return true;
				}
			}
		} catch (e) {
			return true;
		}
	}
	return false;
}());
var equalsConstructorPrototypeIfNotBuggy = function (o) {
	/* global window */
	if (typeof window === 'undefined' || !hasAutomationEqualityBug) {
		return equalsConstructorPrototype(o);
	}
	try {
		return equalsConstructorPrototype(o);
	} catch (e) {
		return false;
	}
};

var keysShim = function keys(object) {
	var isObject = object !== null && typeof object === 'object';
	var isFunction = toStr.call(object) === '[object Function]';
	var isArguments = isArgs(object);
	var isString = isObject && toStr.call(object) === '[object String]';
	var theKeys = [];

	if (!isObject && !isFunction && !isArguments) {
		throw new TypeError('Object.keys called on a non-object');
	}

	var skipProto = hasProtoEnumBug && isFunction;
	if (isString && object.length > 0 && !has.call(object, 0)) {
		for (var i = 0; i < object.length; ++i) {
			theKeys.push(String(i));
		}
	}

	if (isArguments && object.length > 0) {
		for (var j = 0; j < object.length; ++j) {
			theKeys.push(String(j));
		}
	} else {
		for (var name in object) {
			if (!(skipProto && name === 'prototype') && has.call(object, name)) {
				theKeys.push(String(name));
			}
		}
	}

	if (hasDontEnumBug) {
		var skipConstructor = equalsConstructorPrototypeIfNotBuggy(object);

		for (var k = 0; k < dontEnums.length; ++k) {
			if (!(skipConstructor && dontEnums[k] === 'constructor') && has.call(object, dontEnums[k])) {
				theKeys.push(dontEnums[k]);
			}
		}
	}
	return theKeys;
};

keysShim.shim = function shimObjectKeys() {
	if (Object.keys) {
		var keysWorksWithArguments = (function () {
			// Safari 5.0 bug
			return (Object.keys(arguments) || '').length === 2;
		}(1, 2));
		if (!keysWorksWithArguments) {
			var originalKeys = Object.keys;
			Object.keys = function keys(object) {
				if (isArgs(object)) {
					return originalKeys(slice.call(object));
				} else {
					return originalKeys(object);
				}
			};
		}
	} else {
		Object.keys = keysShim;
	}
	return Object.keys || keysShim;
};

module.exports = keysShim;

},{"./isArguments":50}],50:[function(_dereq_,module,exports){
'use strict';

var toStr = Object.prototype.toString;

module.exports = function isArguments(value) {
	var str = toStr.call(value);
	var isArgs = str === '[object Arguments]';
	if (!isArgs) {
		isArgs = str !== '[object Array]' &&
			value !== null &&
			typeof value === 'object' &&
			typeof value.length === 'number' &&
			value.length >= 0 &&
			toStr.call(value.callee) === '[object Function]';
	}
	return isArgs;
};

},{}],51:[function(_dereq_,module,exports){
'use strict';

var keys = _dereq_('object-keys');

module.exports = function hasSymbols() {
	if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; }
	if (typeof Symbol.iterator === 'symbol') { return true; }

	var obj = {};
	var sym = Symbol('test');
	var symObj = Object(sym);
	if (typeof sym === 'string') { return false; }

	if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; }
	if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; }

	// temp disabled per https://github.com/ljharb/object.assign/issues/17
	// if (sym instanceof Symbol) { return false; }
	// temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4
	// if (!(symObj instanceof Symbol)) { return false; }

	var symVal = 42;
	obj[sym] = symVal;
	for (sym in obj) { return false; }
	if (keys(obj).length !== 0) { return false; }
	if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; }

	if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; }

	var syms = Object.getOwnPropertySymbols(obj);
	if (syms.length !== 1 || syms[0] !== sym) { return false; }

	if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; }

	if (typeof Object.getOwnPropertyDescriptor === 'function') {
		var descriptor = Object.getOwnPropertyDescriptor(obj, sym);
		if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; }
	}

	return true;
};

},{"object-keys":49}],52:[function(_dereq_,module,exports){
'use strict';

// modified from https://github.com/es-shims/es6-shim
var keys = _dereq_('object-keys');
var bind = _dereq_('function-bind');
var canBeObject = function (obj) {
	return typeof obj !== 'undefined' && obj !== null;
};
var hasSymbols = _dereq_('./hasSymbols')();
var toObject = Object;
var push = bind.call(Function.call, Array.prototype.push);
var propIsEnumerable = bind.call(Function.call, Object.prototype.propertyIsEnumerable);
var originalGetSymbols = hasSymbols ? Object.getOwnPropertySymbols : null;

module.exports = function assign(target, source1) {
	if (!canBeObject(target)) { throw new TypeError('target must be an object'); }
	var objTarget = toObject(target);
	var s, source, i, props, syms, value, key;
	for (s = 1; s < arguments.length; ++s) {
		source = toObject(arguments[s]);
		props = keys(source);
		var getSymbols = hasSymbols && (Object.getOwnPropertySymbols || originalGetSymbols);
		if (getSymbols) {
			syms = getSymbols(source);
			for (i = 0; i < syms.length; ++i) {
				key = syms[i];
				if (propIsEnumerable(source, key)) {
					push(props, key);
				}
			}
		}
		for (i = 0; i < props.length; ++i) {
			key = props[i];
			value = source[key];
			if (propIsEnumerable(source, key)) {
				objTarget[key] = value;
			}
		}
	}
	return objTarget;
};

},{"./hasSymbols":51,"function-bind":6,"object-keys":49}],53:[function(_dereq_,module,exports){
'use strict';

var defineProperties = _dereq_('define-properties');

var implementation = _dereq_('./implementation');
var getPolyfill = _dereq_('./polyfill');
var shim = _dereq_('./shim');

var polyfill = getPolyfill();

defineProperties(polyfill, {
	implementation: implementation,
	getPolyfill: getPolyfill,
	shim: shim
});

module.exports = polyfill;

},{"./implementation":52,"./polyfill":54,"./shim":55,"define-properties":2}],54:[function(_dereq_,module,exports){
'use strict';

var implementation = _dereq_('./implementation');

var lacksProperEnumerationOrder = function () {
	if (!Object.assign) {
		return false;
	}
	// v8, specifically in node 4.x, has a bug with incorrect property enumeration order
	// note: this does not detect the bug unless there's 20 characters
	var str = 'abcdefghijklmnopqrst';
	var letters = str.split('');
	var map = {};
	for (var i = 0; i < letters.length; ++i) {
		map[letters[i]] = letters[i];
	}
	var obj = Object.assign({}, map);
	var actual = '';
	for (var k in obj) {
		actual += k;
	}
	return str !== actual;
};

var assignHasPendingExceptions = function () {
	if (!Object.assign || !Object.preventExtensions) {
		return false;
	}
	// Firefox 37 still has "pending exception" logic in its Object.assign implementation,
	// which is 72% slower than our shim, and Firefox 40's native implementation.
	var thrower = Object.preventExtensions({ 1: 2 });
	try {
		Object.assign(thrower, 'xy');
	} catch (e) {
		return thrower[1] === 'y';
	}
	return false;
};

module.exports = function getPolyfill() {
	if (!Object.assign) {
		return implementation;
	}
	if (lacksProperEnumerationOrder()) {
		return implementation;
	}
	if (assignHasPendingExceptions()) {
		return implementation;
	}
	return Object.assign;
};

},{"./implementation":52}],55:[function(_dereq_,module,exports){
'use strict';

var define = _dereq_('define-properties');
var getPolyfill = _dereq_('./polyfill');

module.exports = function shimAssign() {
	var polyfill = getPolyfill();
	define(
		Object,
		{ assign: polyfill },
		{ assign: function () { return Object.assign !== polyfill; } }
	);
	return polyfill;
};

},{"./polyfill":54,"define-properties":2}],56:[function(_dereq_,module,exports){
var trim = _dereq_('trim')
  , forEach = _dereq_('for-each')
  , isArray = function(arg) {
      return Object.prototype.toString.call(arg) === '[object Array]';
    }

module.exports = function (headers) {
  if (!headers)
    return {}

  var result = {}

  forEach(
      trim(headers).split('\n')
    , function (row) {
        var index = row.indexOf(':')
          , key = trim(row.slice(0, index)).toLowerCase()
          , value = trim(row.slice(index + 1))

        if (typeof(result[key]) === 'undefined') {
          result[key] = value
        } else if (isArray(result[key])) {
          result[key].push(value)
        } else {
          result[key] = [ result[key], value ]
        }
      }
  )

  return result
}
},{"for-each":3,"trim":58}],57:[function(_dereq_,module,exports){
module.exports = SafeParseTuple

function SafeParseTuple(obj, reviver) {
    var json
    var error = null

    try {
        json = JSON.parse(obj, reviver)
    } catch (err) {
        error = err
    }

    return [error, json]
}

},{}],58:[function(_dereq_,module,exports){

exports = module.exports = trim;

function trim(str){
  return str.replace(/^\s*|\s*$/g, '');
}

exports.left = function(str){
  return str.replace(/^\s*/, '');
};

exports.right = function(str){
  return str.replace(/\s*$/, '');
};

},{}],59:[function(_dereq_,module,exports){
function clean (s) {
  return s.replace(/\n\r?\s*/g, '')
}


module.exports = function tsml (sa) {
  var s = ''
    , i = 0

  for (; i < arguments.length; i++)
    s += clean(sa[i]) + (arguments[i + 1] || '')

  return s
}
},{}],60:[function(_dereq_,module,exports){
"use strict";
var window = _dereq_("global/window")
var isFunction = _dereq_("is-function")
var parseHeaders = _dereq_("parse-headers")
var xtend = _dereq_("xtend")

module.exports = createXHR
createXHR.XMLHttpRequest = window.XMLHttpRequest || noop
createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window.XDomainRequest

forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) {
    createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) {
        options = initParams(uri, options, callback)
        options.method = method.toUpperCase()
        return _createXHR(options)
    }
})

function forEachArray(array, iterator) {
    for (var i = 0; i < array.length; i++) {
        iterator(array[i])
    }
}

function isEmpty(obj){
    for(var i in obj){
        if(obj.hasOwnProperty(i)) return false
    }
    return true
}

function initParams(uri, options, callback) {
    var params = uri

    if (isFunction(options)) {
        callback = options
        if (typeof uri === "string") {
            params = {uri:uri}
        }
    } else {
        params = xtend(options, {uri: uri})
    }

    params.callback = callback
    return params
}

function createXHR(uri, options, callback) {
    options = initParams(uri, options, callback)
    return _createXHR(options)
}

function _createXHR(options) {
    if(typeof options.callback === "undefined"){
        throw new Error("callback argument missing")
    }

    var called = false
    var callback = function cbOnce(err, response, body){
        if(!called){
            called = true
            options.callback(err, response, body)
        }
    }

    function readystatechange() {
        if (xhr.readyState === 4) {
            loadFunc()
        }
    }

    function getBody() {
        // Chrome with requestType=blob throws errors arround when even testing access to responseText
        var body = undefined

        if (xhr.response) {
            body = xhr.response
        } else {
            body = xhr.responseText || getXml(xhr)
        }

        if (isJson) {
            try {
                body = JSON.parse(body)
            } catch (e) {}
        }

        return body
    }

    var failureResponse = {
                body: undefined,
                headers: {},
                statusCode: 0,
                method: method,
                url: uri,
                rawRequest: xhr
            }

    function errorFunc(evt) {
        clearTimeout(timeoutTimer)
        if(!(evt instanceof Error)){
            evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") )
        }
        evt.statusCode = 0
        return callback(evt, failureResponse)
    }

    // will load the data & process the response in a special response object
    function loadFunc() {
        if (aborted) return
        var status
        clearTimeout(timeoutTimer)
        if(options.useXDR && xhr.status===undefined) {
            //IE8 CORS GET successful response doesn't have a status field, but body is fine
            status = 200
        } else {
            status = (xhr.status === 1223 ? 204 : xhr.status)
        }
        var response = failureResponse
        var err = null

        if (status !== 0){
            response = {
                body: getBody(),
                statusCode: status,
                method: method,
                headers: {},
                url: uri,
                rawRequest: xhr
            }
            if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE
                response.headers = parseHeaders(xhr.getAllResponseHeaders())
            }
        } else {
            err = new Error("Internal XMLHttpRequest Error")
        }
        return callback(err, response, response.body)
    }

    var xhr = options.xhr || null

    if (!xhr) {
        if (options.cors || options.useXDR) {
            xhr = new createXHR.XDomainRequest()
        }else{
            xhr = new createXHR.XMLHttpRequest()
        }
    }

    var key
    var aborted
    var uri = xhr.url = options.uri || options.url
    var method = xhr.method = options.method || "GET"
    var body = options.body || options.data || null
    var headers = xhr.headers = options.headers || {}
    var sync = !!options.sync
    var isJson = false
    var timeoutTimer

    if ("json" in options) {
        isJson = true
        headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json") //Don't override existing accept header declared by user
        if (method !== "GET" && method !== "HEAD") {
            headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json") //Don't override existing accept header declared by user
            body = JSON.stringify(options.json)
        }
    }

    xhr.onreadystatechange = readystatechange
    xhr.onload = loadFunc
    xhr.onerror = errorFunc
    // IE9 must have onprogress be set to a unique function.
    xhr.onprogress = function () {
        // IE must die
    }
    xhr.ontimeout = errorFunc
    xhr.open(method, uri, !sync, options.username, options.password)
    //has to be after open
    if(!sync) {
        xhr.withCredentials = !!options.withCredentials
    }
    // Cannot set timeout with sync request
    // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
    // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
    if (!sync && options.timeout > 0 ) {
        timeoutTimer = setTimeout(function(){
            aborted=true//IE9 may still call readystatechange
            xhr.abort("timeout")
            var e = new Error("XMLHttpRequest timeout")
            e.code = "ETIMEDOUT"
            errorFunc(e)
        }, options.timeout )
    }

    if (xhr.setRequestHeader) {
        for(key in headers){
            if(headers.hasOwnProperty(key)){
                xhr.setRequestHeader(key, headers[key])
            }
        }
    } else if (options.headers && !isEmpty(options.headers)) {
        throw new Error("Headers cannot be set on an XDomainRequest object")
    }

    if ("responseType" in options) {
        xhr.responseType = options.responseType
    }

    if ("beforeSend" in options &&
        typeof options.beforeSend === "function"
    ) {
        options.beforeSend(xhr)
    }

    xhr.send(body)

    return xhr


}

function getXml(xhr) {
    if (xhr.responseType === "document") {
        return xhr.responseXML
    }
    var firefoxBugTakenEffect = xhr.status === 204 && xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror"
    if (xhr.responseType === "" && !firefoxBugTakenEffect) {
        return xhr.responseXML
    }

    return null
}

function noop() {}

},{"global/window":8,"is-function":9,"parse-headers":56,"xtend":61}],61:[function(_dereq_,module,exports){
module.exports = extend

var hasOwnProperty = Object.prototype.hasOwnProperty;

function extend() {
    var target = {}

    for (var i = 0; i < arguments.length; i++) {
        var source = arguments[i]

        for (var key in source) {
            if (hasOwnProperty.call(source, key)) {
                target[key] = source[key]
            }
        }
    }

    return target
}

},{}],62:[function(_dereq_,module,exports){
/**
 * @file big-play-button.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _buttonJs = _dereq_('./button.js');

var _buttonJs2 = _interopRequireDefault(_buttonJs);

var _componentJs = _dereq_('./component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

/**
 * Initial play button. Shows before the video has played. The hiding of the
 * big play button is done via CSS and player states.
 *
 * @param {Object} player  Main Player
 * @param {Object=} options Object of option names and values
 * @extends Button
 * @class BigPlayButton
 */

var BigPlayButton = (function (_Button) {
  _inherits(BigPlayButton, _Button);

  function BigPlayButton(player, options) {
    _classCallCheck(this, BigPlayButton);

    _Button.call(this, player, options);
  }

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  BigPlayButton.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-big-play-button';
  };

  /**
   * Handles click for play
   *
   * @method handleClick
   */

  BigPlayButton.prototype.handleClick = function handleClick() {
    this.player_.play();
  };

  /**
   * Return handler for the tooltip
   *
   * @return {Object} Dom element to serve as a handler for the tooltip
   * @method tooltipHandler
   */

  BigPlayButton.prototype.tooltipHandler = function tooltipHandler() {
    if (this.options_.tooltip !== undefined) {
      return _Button.prototype.tooltipHandler.call(this);
    }
  };

  return BigPlayButton;
})(_buttonJs2['default']);

BigPlayButton.prototype.controlText_ = 'Play Video';

_componentJs2['default'].registerComponent('BigPlayButton', BigPlayButton);
exports['default'] = BigPlayButton;
module.exports = exports['default'];

},{"./button.js":63,"./component.js":66}],63:[function(_dereq_,module,exports){
/**
 * @file button.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _clickableComponentJs = _dereq_('./clickable-component.js');

var _clickableComponentJs2 = _interopRequireDefault(_clickableComponentJs);

var _component = _dereq_('./component');

var _component2 = _interopRequireDefault(_component);

var _utilsEventsJs = _dereq_('./utils/events.js');

var Events = _interopRequireWildcard(_utilsEventsJs);

var _utilsFnJs = _dereq_('./utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsLogJs = _dereq_('./utils/log.js');

var _utilsLogJs2 = _interopRequireDefault(_utilsLogJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _objectAssign = _dereq_('object.assign');

var _objectAssign2 = _interopRequireDefault(_objectAssign);

/**
 * Base class for all buttons
 *
 * @param {Object} player  Main Player
 * @param {Object=} options Object of option names and values
 * @extends ClickableComponent
 * @class Button
 */

var Button = (function (_ClickableComponent) {
  _inherits(Button, _ClickableComponent);

  function Button(player, options) {
    _classCallCheck(this, Button);

    _ClickableComponent.call(this, player, options);
  }

  /**
   * Create the component's DOM element
   *
   * @param {String=} type Element's node type. e.g. 'div'
   * @param {Object=} props An object of properties that should be set on the element
   * @param {Object=} attributes An object of attributes that should be set on the element
   * @return {Element}
   * @method createEl
   */

  Button.prototype.createEl = function createEl() {
    var tag = arguments.length <= 0 || arguments[0] === undefined ? 'button' : arguments[0];
    var props = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
    var attributes = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];

    props = _objectAssign2['default']({
      className: this.buildCSSClass()
    }, props);

    if (tag !== 'button') {
      _utilsLogJs2['default'].warn('Creating a Button with an HTML element of ' + tag + ' is deprecated; use ClickableComponent instead.');

      // Add properties for clickable element which is not a native HTML button
      props = _objectAssign2['default']({
        tabIndex: 0
      }, props);

      // Add ARIA attributes for clickable element which is not a native HTML button
      attributes = _objectAssign2['default']({
        role: 'button'
      }, attributes);
    }

    // Add attributes for button element
    attributes = _objectAssign2['default']({
      type: 'button', // Necessary since the default button type is "submit"
      'aria-live': 'polite' // let the screen reader user know that the text of the button may change
    }, attributes);

    var el = _component2['default'].prototype.createEl.call(this, tag, props, attributes);

    this.createControlTextEl(el);

    return el;
  };

  /**
   * Adds a child component inside this button
   *
   * @param {String|Component} child The class name or instance of a child to add
   * @param {Object=} options Options, including options to be passed to children of the child.
   * @return {Component} The child component (created by this process if a string was used)
   * @deprecated
   * @method addChild
   */

  Button.prototype.addChild = function addChild(child) {
    var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

    var className = this.constructor.name;
    _utilsLogJs2['default'].warn('Adding an actionable (user controllable) child to a Button (' + className + ') is not supported; use a ClickableComponent instead.');

    // Avoid the error message generated by ClickableComponent's addChild method
    return _component2['default'].prototype.addChild.call(this, child, options);
  };

  /**
   * Handle KeyPress (document level) - Extend with specific functionality for button
   *
   * @method handleKeyPress
   */

  Button.prototype.handleKeyPress = function handleKeyPress(event) {
    // Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
    if (event.which === 32 || event.which === 13) {} else {
      _ClickableComponent.prototype.handleKeyPress.call(this, event); // Pass keypress handling up for unsupported keys
    }
  };

  return Button;
})(_clickableComponentJs2['default']);

_component2['default'].registerComponent('Button', Button);
exports['default'] = Button;
module.exports = exports['default'];

},{"./clickable-component.js":64,"./component":66,"./utils/events.js":145,"./utils/fn.js":146,"./utils/log.js":149,"global/document":7,"object.assign":53}],64:[function(_dereq_,module,exports){
/**
 * @file button.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _component = _dereq_('./component');

var _component2 = _interopRequireDefault(_component);

var _utilsDomJs = _dereq_('./utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsEventsJs = _dereq_('./utils/events.js');

var Events = _interopRequireWildcard(_utilsEventsJs);

var _utilsFnJs = _dereq_('./utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsLogJs = _dereq_('./utils/log.js');

var _utilsLogJs2 = _interopRequireDefault(_utilsLogJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _objectAssign = _dereq_('object.assign');

var _objectAssign2 = _interopRequireDefault(_objectAssign);

var _tooltip = _dereq_('./tooltip');

var _tooltip2 = _interopRequireDefault(_tooltip);

/**
 * Clickable Component which is clickable or keyboard actionable, but is not a native HTML button
 *
 * @param {Object} player  Main Player
 * @param {Object=} options Object of option names and values
 * @extends Component
 * @class ClickableComponent
 */

var ClickableComponent = (function (_Component) {
  _inherits(ClickableComponent, _Component);

  function ClickableComponent(player, options) {
    _classCallCheck(this, ClickableComponent);

    _Component.call(this, player, options);

    this.appendTooltip();

    this.emitTapEvents();

    this.on('tap', this.handleClick);
    this.on('click', this.handleClick);
    this.on('focus', this.handleFocus);
    this.on('blur', this.handleBlur);
  }

  /**
   * Create the component's DOM element
   *
   * @param {String=} type Element's node type. e.g. 'div'
   * @param {Object=} props An object of properties that should be set on the element
   * @param {Object=} attributes An object of attributes that should be set on the element
   * @return {Element}
   * @method createEl
   */

  ClickableComponent.prototype.createEl = function createEl() {
    var tag = arguments.length <= 0 || arguments[0] === undefined ? 'div' : arguments[0];
    var props = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
    var attributes = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];

    props = _objectAssign2['default']({
      className: this.buildCSSClass(),
      tabIndex: 0
    }, props);

    if (tag === 'button') {
      _utilsLogJs2['default'].error('Creating a ClickableComponent with an HTML element of ' + tag + ' is not supported; use a Button instead.');
    }

    // Add ARIA attributes for clickable element which is not a native HTML button
    attributes = _objectAssign2['default']({
      role: 'button',
      'aria-live': 'polite' // let the screen reader user know that the text of the element may change
    }, attributes);

    var el = _Component.prototype.createEl.call(this, tag, props, attributes);

    this.createControlTextEl(el);

    return el;
  };

  /**
   * create control text
   *
   * @param {Element} el Parent element for the control text
   * @return {Element}
   * @method controlText
   */

  ClickableComponent.prototype.createControlTextEl = function createControlTextEl(el) {
    this.controlTextEl_ = Dom.createEl('span', {
      className: 'vjs-control-text'
    });

    if (el) {
      el.appendChild(this.controlTextEl_);
    }

    this.controlText(this.controlText_);

    return this.controlTextEl_;
  };

  /**
   * Controls text - both request and localize
   *
   * @param {String} text Text for element
   * @return {String}
   * @method controlText
   */

  ClickableComponent.prototype.controlText = function controlText(text) {
    if (!text) return this.controlText_ || 'Need Text';

    this.controlText_ = text;
    this.controlTextEl_.innerHTML = this.localize(this.controlText_);

    if (this.tooltip) {
      this.tooltip.text(this.controlText_);
    } else {
      this.appendTooltip();
    }

    return this;
  };

  /**
   * Allows sub components to stack CSS class names
   *
   * @return {String}
   * @method buildCSSClass
   */

  ClickableComponent.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this);
  };

  /**
   * Adds a child component inside this clickable-component
   *
   * @param {String|Component} child The class name or instance of a child to add
   * @param {Object=} options Options, including options to be passed to children of the child.
   * @return {Component} The child component (created by this process if a string was used)
   * @method addChild
   */

  ClickableComponent.prototype.addChild = function addChild(child) {
    var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

    // TODO: Fix adding an actionable child to a ClickableComponent; currently
    // it will cause issues with assistive technology (e.g. screen readers)
    // which support ARIA, since an element with role="button" cannot have
    // actionable child elements.

    //let className = this.constructor.name;
    //log.warn(`Adding a child to a ClickableComponent (${className}) can cause issues with assistive technology which supports ARIA, since an element with role="button" cannot have actionable child elements.`);

    return _Component.prototype.addChild.call(this, child, options);
  };

  /**
   * Enable the component element
   *
   * @return {Component}
   * @method enable
   */

  ClickableComponent.prototype.enable = function enable() {
    this.removeClass('vjs-disabled');
    this.el_.setAttribute('aria-disabled', 'false');
    return this;
  };

  /**
   * Disable the component element
   *
   * @return {Component}
   * @method disable
   */

  ClickableComponent.prototype.disable = function disable() {
    this.addClass('vjs-disabled');
    this.el_.setAttribute('aria-disabled', 'true');
    return this;
  };

  /**
   * Handle Click - Override with specific functionality for component
   *
   * @method handleClick
   */

  ClickableComponent.prototype.handleClick = function handleClick() {};

  /**
   * Handle Focus - Add keyboard functionality to element
   *
   * @method handleFocus
   */

  ClickableComponent.prototype.handleFocus = function handleFocus() {
    Events.on(_globalDocument2['default'], 'keydown', Fn.bind(this, this.handleKeyPress));
  };

  /**
   * Handle KeyPress (document level) - Trigger click when Space or Enter key is pressed
   *
   * @method handleKeyPress
   */

  ClickableComponent.prototype.handleKeyPress = function handleKeyPress(event) {
    // Support Space (32) or Enter (13) key operation to fire a click event
    if (event.which === 32 || event.which === 13) {
      event.preventDefault();
      this.handleClick(event);
    } else if (_Component.prototype.handleKeyPress) {
      _Component.prototype.handleKeyPress.call(this, event); // Pass keypress handling up for unsupported keys
    }
  };

  /**
   * Handle Blur - Remove keyboard triggers
   *
   * @method handleBlur
   */

  ClickableComponent.prototype.handleBlur = function handleBlur() {
    Events.off(_globalDocument2['default'], 'keydown', Fn.bind(this, this.handleKeyPress));
  };

  /**
   * Append a tooltip to the component
   *
   * @method appendTooltip
   */

  ClickableComponent.prototype.appendTooltip = function appendTooltip() {
    if (this.tooltip || !this.controlText_ || !this.el()) {
      return;
    }

    var tooltipHandler = this.tooltipHandler();

    if (tooltipHandler) {
      this.tooltip = new _tooltip2['default'](this.player_);
      this.el().appendChild(this.tooltip.el());
      this.tooltip.text(this.controlText_);
      this.tooltip.handler(tooltipHandler);
    }
  };

  /**
   * Return handler for the tooltip
   *
   * @return {Object} Dom element to serve as a handler for the tooltip
   * @method tooltipHandler
   */

  ClickableComponent.prototype.tooltipHandler = function tooltipHandler() {
    var handler = this.options_.tooltip;

    if (handler === false) {
      return;
    }

    if (handler === true || this.player_.options_ && this.player_.options_.tooltips) {
      return this.el();
    }

    if (typeof handler === 'string') {
      return Dom.getEl(handler);
    }

    if (handler && typeof handler.el === 'function') {
      return handler.el();
    }

    return handler;
  };

  return ClickableComponent;
})(_component2['default']);

_component2['default'].registerComponent('ClickableComponent', ClickableComponent);
exports['default'] = ClickableComponent;
module.exports = exports['default'];

},{"./component":66,"./tooltip":125,"./utils/dom.js":144,"./utils/events.js":145,"./utils/fn.js":146,"./utils/log.js":149,"global/document":7,"object.assign":53}],65:[function(_dereq_,module,exports){
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _button = _dereq_('./button');

var _button2 = _interopRequireDefault(_button);

var _component = _dereq_('./component');

var _component2 = _interopRequireDefault(_component);

/**
 * The `CloseButton` component is a button which fires a "close" event
 * when it is activated.
 *
 * @extends Button
 * @class CloseButton
 */

var CloseButton = (function (_Button) {
  _inherits(CloseButton, _Button);

  function CloseButton(player, options) {
    _classCallCheck(this, CloseButton);

    _Button.call(this, player, options);
    this.controlText(options && options.controlText || this.localize('Close'));
  }

  CloseButton.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-close-button ' + _Button.prototype.buildCSSClass.call(this);
  };

  CloseButton.prototype.handleClick = function handleClick() {
    this.trigger({ type: 'close', bubbles: false });
  };

  return CloseButton;
})(_button2['default']);

_component2['default'].registerComponent('CloseButton', CloseButton);
exports['default'] = CloseButton;
module.exports = exports['default'];

},{"./button":63,"./component":66}],66:[function(_dereq_,module,exports){
/**
 * @file component.js
 *
 * Player Component - Base class for all UI objects
 */

'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _utilsDomJs = _dereq_('./utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFnJs = _dereq_('./utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsGuidJs = _dereq_('./utils/guid.js');

var Guid = _interopRequireWildcard(_utilsGuidJs);

var _utilsEventsJs = _dereq_('./utils/events.js');

var Events = _interopRequireWildcard(_utilsEventsJs);

var _utilsLogJs = _dereq_('./utils/log.js');

var _utilsLogJs2 = _interopRequireDefault(_utilsLogJs);

var _utilsToTitleCaseJs = _dereq_('./utils/to-title-case.js');

var _utilsToTitleCaseJs2 = _interopRequireDefault(_utilsToTitleCaseJs);

var _utilsMergeOptionsJs = _dereq_('./utils/merge-options.js');

var _utilsMergeOptionsJs2 = _interopRequireDefault(_utilsMergeOptionsJs);

/**
 * Base UI Component class
 * Components are embeddable UI objects that are represented by both a
 * javascript object and an element in the DOM. They can be children of other
 * components, and can have many children themselves.
 * ```js
 *     // adding a button to the player
 *     var button = player.addChild('button');
 *     button.el(); // -> button element
 * ```
 * ```html
 *     <div class="video-js">
 *       <div class="vjs-button">Button</div>
 *     </div>
 * ```
 * Components are also event targets.
 * ```js
 *     button.on('click', function(){
 *       console.log('Button Clicked!');
 *     });
 *     button.trigger('customevent');
 * ```
 *
 * @param {Object} player  Main Player
 * @param {Object=} options Object of option names and values
 * @param {Function=} ready    Ready callback function
 * @class Component
 */

var Component = (function () {
  function Component(player, options, ready) {
    _classCallCheck(this, Component);

    // The component might be the player itself and we can't pass `this` to super
    if (!player && this.play) {
      this.player_ = player = this; // eslint-disable-line
    } else {
        this.player_ = player;
      }

    // Make a copy of prototype.options_ to protect against overriding defaults
    this.options_ = _utilsMergeOptionsJs2['default']({}, this.options_);

    // Updated options with supplied options
    options = this.options_ = _utilsMergeOptionsJs2['default'](this.options_, options);

    // Get ID from options or options element if one is supplied
    this.id_ = options.id || options.el && options.el.id;

    // If there was no ID from the options, generate one
    if (!this.id_) {
      // Don't require the player ID function in the case of mock players
      var id = player && player.id && player.id() || 'no_player';

      this.id_ = id + '_component_' + Guid.newGUID();
    }

    this.name_ = options.name || null;

    // Create element if one wasn't provided in options
    if (options.el) {
      this.el_ = options.el;
    } else if (options.createEl !== false) {
      this.el_ = this.createEl();
    }

    this.children_ = [];
    this.childIndex_ = {};
    this.childNameIndex_ = {};

    // Add any child components in options
    if (options.initChildren !== false) {
      this.initChildren();
    }

    this.ready(ready);
    // Don't want to trigger ready here or it will before init is actually
    // finished for all children that run this constructor

    if (options.reportTouchActivity !== false) {
      this.enableTouchActivity();
    }
  }

  /**
   * Dispose of the component and all child components
   *
   * @method dispose
   */

  Component.prototype.dispose = function dispose() {
    this.trigger({ type: 'dispose', bubbles: false });

    // Dispose all children.
    if (this.children_) {
      for (var i = this.children_.length - 1; i >= 0; i--) {
        if (this.children_[i].dispose) {
          this.children_[i].dispose();
        }
      }
    }

    // Delete child references
    this.children_ = null;
    this.childIndex_ = null;
    this.childNameIndex_ = null;

    // Remove all event listeners.
    this.off();

    // Remove element from DOM
    if (this.el_.parentNode) {
      this.el_.parentNode.removeChild(this.el_);
    }

    Dom.removeElData(this.el_);
    this.el_ = null;
  };

  /**
   * Return the component's player
   *
   * @return {Player}
   * @method player
   */

  Component.prototype.player = function player() {
    return this.player_;
  };

  /**
   * Deep merge of options objects
   * Whenever a property is an object on both options objects
   * the two properties will be merged using mergeOptions.
   *
   * ```js
   *     Parent.prototype.options_ = {
   *       optionSet: {
   *         'childOne': { 'foo': 'bar', 'asdf': 'fdsa' },
   *         'childTwo': {},
   *         'childThree': {}
   *       }
   *     }
   *     newOptions = {
   *       optionSet: {
   *         'childOne': { 'foo': 'baz', 'abc': '123' }
   *         'childTwo': null,
   *         'childFour': {}
   *       }
   *     }
   *
   *     this.options(newOptions);
   * ```
   * RESULT
   * ```js
   *     {
   *       optionSet: {
   *         'childOne': { 'foo': 'baz', 'asdf': 'fdsa', 'abc': '123' },
   *         'childTwo': null, // Disabled. Won't be initialized.
   *         'childThree': {},
   *         'childFour': {}
   *       }
   *     }
   * ```
   *
   * @param  {Object} obj Object of new option values
   * @return {Object}     A NEW object of this.options_ and obj merged
   * @method options
   */

  Component.prototype.options = function options(obj) {
    _utilsLogJs2['default'].warn('this.options() has been deprecated and will be moved to the constructor in 6.0');

    if (!obj) {
      return this.options_;
    }

    this.options_ = _utilsMergeOptionsJs2['default'](this.options_, obj);
    return this.options_;
  };

  /**
   * Get the component's DOM element
   * ```js
   *     var domEl = myComponent.el();
   * ```
   *
   * @return {Element}
   * @method el
   */

  Component.prototype.el = function el() {
    return this.el_;
  };

  /**
   * Create the component's DOM element
   *
   * @param  {String=} tagName  Element's node type. e.g. 'div'
   * @param  {Object=} properties An object of properties that should be set
   * @param  {Object=} attributes An object of attributes that should be set
   * @return {Element}
   * @method createEl
   */

  Component.prototype.createEl = function createEl(tagName, properties, attributes) {
    return Dom.createEl(tagName, properties, attributes);
  };

  Component.prototype.localize = function localize(string) {
    var code = this.player_.language && this.player_.language();
    var languages = this.player_.languages && this.player_.languages();

    if (!code || !languages) {
      return string;
    }

    var language = languages[code];

    if (language && language[string]) {
      return language[string];
    }

    var primaryCode = code.split('-')[0];
    var primaryLang = languages[primaryCode];

    if (primaryLang && primaryLang[string]) {
      return primaryLang[string];
    }

    return string;
  };

  /**
   * Return the component's DOM element where children are inserted.
   * Will either be the same as el() or a new element defined in createEl().
   *
   * @return {Element}
   * @method contentEl
   */

  Component.prototype.contentEl = function contentEl() {
    return this.contentEl_ || this.el_;
  };

  /**
   * Get the component's ID
   * ```js
   *     var id = myComponent.id();
   * ```
   *
   * @return {String}
   * @method id
   */

  Component.prototype.id = function id() {
    return this.id_;
  };

  /**
   * Get the component's name. The name is often used to reference the component.
   * ```js
   *     var name = myComponent.name();
   * ```
   *
   * @return {String}
   * @method name
   */

  Component.prototype.name = function name() {
    return this.name_;
  };

  /**
   * Get an array of all child components
   * ```js
   *     var kids = myComponent.children();
   * ```
   *
   * @return {Array} The children
   * @method children
   */

  Component.prototype.children = function children() {
    return this.children_;
  };

  /**
   * Returns a child component with the provided ID
   *
   * @return {Component}
   * @method getChildById
   */

  Component.prototype.getChildById = function getChildById(id) {
    return this.childIndex_[id];
  };

  /**
   * Returns a child component with the provided name
   *
   * @return {Component}
   * @method getChild
   */

  Component.prototype.getChild = function getChild(name) {
    return this.childNameIndex_[name];
  };

  /**
   * Adds a child component inside this component
   * ```js
   *     myComponent.el();
   *     // -> <div class='my-component'></div>
   *     myComponent.children();
   *     // [empty array]
   *
   *     var myButton = myComponent.addChild('MyButton');
   *     // -> <div class='my-component'><div class="my-button">myButton<div></div>
   *     // -> myButton === myComponent.children()[0];
   * ```
   * Pass in options for child constructors and options for children of the child
   * ```js
   *     var myButton = myComponent.addChild('MyButton', {
   *       text: 'Press Me',
   *       buttonChildExample: {
   *         buttonChildOption: true
   *       }
   *     });
   * ```
   *
   * @param {String|Component} child The class name or instance of a child to add
   * @param {Object=} options Options, including options to be passed to children of the child.
   * @param {Number} index into our children array to attempt to add the child
   * @return {Component} The child component (created by this process if a string was used)
   * @method addChild
   */

  Component.prototype.addChild = function addChild(child) {
    var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
    var index = arguments.length <= 2 || arguments[2] === undefined ? this.children_.length : arguments[2];

    var component = undefined;
    var componentName = undefined;

    // If child is a string, create nt with options
    if (typeof child === 'string') {
      componentName = child;

      // Options can also be specified as a boolean, so convert to an empty object if false.
      if (!options) {
        options = {};
      }

      // Same as above, but true is deprecated so show a warning.
      if (options === true) {
        _utilsLogJs2['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`.');
        options = {};
      }

      // If no componentClass in options, assume componentClass is the name lowercased
      // (e.g. playButton)
      var componentClassName = options.componentClass || _utilsToTitleCaseJs2['default'](componentName);

      // Set name through options
      options.name = componentName;

      // Create a new object & element for this controls set
      // If there's no .player_, this is a player
      var ComponentClass = Component.getComponent(componentClassName);

      if (!ComponentClass) {
        throw new Error('Component ' + componentClassName + ' does not exist');
      }

      // data stored directly on the videojs object may be
      // misidentified as a component to retain
      // backwards-compatibility with 4.x. check to make sure the
      // component class can be instantiated.
      if (typeof ComponentClass !== 'function') {
        return null;
      }

      component = new ComponentClass(this.player_ || this, options);

      // child is a component instance
    } else {
        component = child;
      }

    this.children_.splice(index, 0, component);

    if (typeof component.id === 'function') {
      this.childIndex_[component.id()] = component;
    }

    // If a name wasn't used to create the component, check if we can use the
    // name function of the component
    componentName = componentName || component.name && component.name();

    if (componentName) {
      this.childNameIndex_[componentName] = component;
    }

    // Add the UI object's element to the container div (box)
    // Having an element is not required
    if (typeof component.el === 'function' && component.el()) {
      var childNodes = this.contentEl().children;
      var refNode = childNodes[index] || null;
      this.contentEl().insertBefore(component.el(), refNode);
    }

    // Return so it can stored on parent object if desired.
    return component;
  };

  /**
   * Remove a child component from this component's list of children, and the
   * child component's element from this component's element
   *
   * @param  {Component} component Component to remove
   * @method removeChild
   */

  Component.prototype.removeChild = function removeChild(component) {
    if (typeof component === 'string') {
      component = this.getChild(component);
    }

    if (!component || !this.children_) {
      return;
    }

    var childFound = false;

    for (var i = this.children_.length - 1; i >= 0; i--) {
      if (this.children_[i] === component) {
        childFound = true;
        this.children_.splice(i, 1);
        break;
      }
    }

    if (!childFound) {
      return;
    }

    this.childIndex_[component.id()] = null;
    this.childNameIndex_[component.name()] = null;

    var compEl = component.el();

    if (compEl && compEl.parentNode === this.contentEl()) {
      this.contentEl().removeChild(component.el());
    }
  };

  /**
   * Add and initialize default child components from options
   * ```js
   *     // when an instance of MyComponent is created, all children in options
   *     // will be added to the instance by their name strings and options
   *     MyComponent.prototype.options_ = {
   *       children: [
   *         'myChildComponent'
   *       ],
   *       myChildComponent: {
   *         myChildOption: true
   *       }
   *     };
   *
   *     // Or when creating the component
   *     var myComp = new MyComponent(player, {
   *       children: [
   *         'myChildComponent'
   *       ],
   *       myChildComponent: {
   *         myChildOption: true
   *       }
   *     });
   * ```
   * The children option can also be an array of
   * child options objects (that also include a 'name' key).
   * This can be used if you have two child components of the
   * same type that need different options.
   * ```js
   *     var myComp = new MyComponent(player, {
   *       children: [
   *         'button',
   *         {
   *           name: 'button',
   *           someOtherOption: true
   *         },
   *         {
   *           name: 'button',
   *           someOtherOption: false
   *         }
   *       ]
   *     });
   * ```
   *
   * @method initChildren
   */

  Component.prototype.initChildren = function initChildren() {
    var _this = this;

    var children = this.options_.children;

    if (children) {
      (function () {
        // `this` is `parent`
        var parentOptions = _this.options_;

        var handleAdd = function handleAdd(child) {
          var name = child.name;
          var opts = child.opts;

          // Allow options for children to be set at the parent options
          // e.g. videojs(id, { controlBar: false });
          // instead of videojs(id, { children: { controlBar: false });
          if (parentOptions[name] !== undefined) {
            opts = parentOptions[name];
          }

          // Allow for disabling default components
          // e.g. options['children']['posterImage'] = false
          if (opts === false) {
            return;
          }

          // Allow options to be passed as a simple boolean if no configuration
          // is necessary.
          if (opts === true) {
            opts = {};
          }

          // We also want to pass the original player options to each component as well so they don't need to
          // reach back into the player for options later.
          opts.playerOptions = _this.options_.playerOptions;

          // Create and add the child component.
          // Add a direct reference to the child by name on the parent instance.
          // If two of the same component are used, different names should be supplied
          // for each
          var newChild = _this.addChild(name, opts);
          if (newChild) {
            _this[name] = newChild;
          }
        };

        // Allow for an array of children details to passed in the options
        var workingChildren = undefined;
        var Tech = Component.getComponent('Tech');

        if (Array.isArray(children)) {
          workingChildren = children;
        } else {
          workingChildren = Object.keys(children);
        }

        workingChildren
        // children that are in this.options_ but also in workingChildren  would
        // give us extra children we do not want. So, we want to filter them out.
        .concat(Object.keys(_this.options_).filter(function (child) {
          return !workingChildren.some(function (wchild) {
            if (typeof wchild === 'string') {
              return child === wchild;
            } else {
              return child === wchild.name;
            }
          });
        })).map(function (child) {
          var name = undefined,
              opts = undefined;

          if (typeof child === 'string') {
            name = child;
            opts = children[name] || _this.options_[name] || {};
          } else {
            name = child.name;
            opts = child;
          }

          return { name: name, opts: opts };
        }).filter(function (child) {
          // we have to make sure that child.name isn't in the techOrder since
          // techs are registerd as Components but can't aren't compatible
          // See https://github.com/videojs/video.js/issues/2772
          var c = Component.getComponent(child.opts.componentClass || _utilsToTitleCaseJs2['default'](child.name));
          return c && !Tech.isTech(c);
        }).forEach(handleAdd);
      })();
    }
  };

  /**
   * Allows sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  Component.prototype.buildCSSClass = function buildCSSClass() {
    // Child classes can include a function that does:
    // return 'CLASS NAME' + this._super();
    return '';
  };

  /**
   * Add an event listener to this component's element
   * ```js
   *     var myFunc = function(){
   *       var myComponent = this;
   *       // Do something when the event is fired
   *     };
   *
   *     myComponent.on('eventType', myFunc);
   * ```
   * The context of myFunc will be myComponent unless previously bound.
   * Alternatively, you can add a listener to another element or component.
   * ```js
   *     myComponent.on(otherElement, 'eventName', myFunc);
   *     myComponent.on(otherComponent, 'eventName', myFunc);
   * ```
   * The benefit of using this over `VjsEvents.on(otherElement, 'eventName', myFunc)`
   * and `otherComponent.on('eventName', myFunc)` is that this way the listeners
   * will be automatically cleaned up when either component is disposed.
   * It will also bind myComponent as the context of myFunc.
   * **NOTE**: When using this on elements in the page other than window
   * and document (both permanent), if you remove the element from the DOM
   * you need to call `myComponent.trigger(el, 'dispose')` on it to clean up
   * references to it and allow the browser to garbage collect it.
   *
   * @param  {String|Component} first   The event type or other component
   * @param  {Function|String}      second  The event handler or event type
   * @param  {Function}             third   The event handler
   * @return {Component}
   * @method on
   */

  Component.prototype.on = function on(first, second, third) {
    var _this2 = this;

    if (typeof first === 'string' || Array.isArray(first)) {
      Events.on(this.el_, first, Fn.bind(this, second));

      // Targeting another component or element
    } else {
        (function () {
          var target = first;
          var type = second;
          var fn = Fn.bind(_this2, third);

          // When this component is disposed, remove the listener from the other component
          var removeOnDispose = function removeOnDispose() {
            return _this2.off(target, type, fn);
          };

          // Use the same function ID so we can remove it later it using the ID
          // of the original listener
          removeOnDispose.guid = fn.guid;
          _this2.on('dispose', removeOnDispose);

          // If the other component is disposed first we need to clean the reference
          // to the other component in this component's removeOnDispose listener
          // Otherwise we create a memory leak.
          var cleanRemover = function cleanRemover() {
            return _this2.off('dispose', removeOnDispose);
          };

          // Add the same function ID so we can easily remove it later
          cleanRemover.guid = fn.guid;

          // Check if this is a DOM node
          if (first.nodeName) {
            // Add the listener to the other element
            Events.on(target, type, fn);
            Events.on(target, 'dispose', cleanRemover);

            // Should be a component
            // Not using `instanceof Component` because it makes mock players difficult
          } else if (typeof first.on === 'function') {
              // Add the listener to the other component
              target.on(type, fn);
              target.on('dispose', cleanRemover);
            }
        })();
      }

    return this;
  };

  /**
   * Remove an event listener from this component's element
   * ```js
   *     myComponent.off('eventType', myFunc);
   * ```
   * If myFunc is excluded, ALL listeners for the event type will be removed.
   * If eventType is excluded, ALL listeners will be removed from the component.
   * Alternatively you can use `off` to remove listeners that were added to other
   * elements or components using `myComponent.on(otherComponent...`.
   * In this case both the event type and listener function are REQUIRED.
   * ```js
   *     myComponent.off(otherElement, 'eventType', myFunc);
   *     myComponent.off(otherComponent, 'eventType', myFunc);
   * ```
   *
   * @param  {String=|Component}  first  The event type or other component
   * @param  {Function=|String}       second The listener function or event type
   * @param  {Function=}              third  The listener for other component
   * @return {Component}
   * @method off
   */

  Component.prototype.off = function off(first, second, third) {
    if (!first || typeof first === 'string' || Array.isArray(first)) {
      Events.off(this.el_, first, second);
    } else {
      var target = first;
      var type = second;
      // Ensure there's at least a guid, even if the function hasn't been used
      var fn = Fn.bind(this, third);

      // Remove the dispose listener on this component,
      // which was given the same guid as the event listener
      this.off('dispose', fn);

      if (first.nodeName) {
        // Remove the listener
        Events.off(target, type, fn);
        // Remove the listener for cleaning the dispose listener
        Events.off(target, 'dispose', fn);
      } else {
        target.off(type, fn);
        target.off('dispose', fn);
      }
    }

    return this;
  };

  /**
   * Add an event listener to be triggered only once and then removed
   * ```js
   *     myComponent.one('eventName', myFunc);
   * ```
   * Alternatively you can add a listener to another element or component
   * that will be triggered only once.
   * ```js
   *     myComponent.one(otherElement, 'eventName', myFunc);
   *     myComponent.one(otherComponent, 'eventName', myFunc);
   * ```
   *
   * @param  {String|Component}  first   The event type or other component
   * @param  {Function|String}       second  The listener function or event type
   * @param  {Function=}             third   The listener function for other component
   * @return {Component}
   * @method one
   */

  Component.prototype.one = function one(first, second, third) {
    var _this3 = this,
        _arguments = arguments;

    if (typeof first === 'string' || Array.isArray(first)) {
      Events.one(this.el_, first, Fn.bind(this, second));
    } else {
      (function () {
        var target = first;
        var type = second;
        var fn = Fn.bind(_this3, third);

        var newFunc = function newFunc() {
          _this3.off(target, type, newFunc);
          fn.apply(null, _arguments);
        };

        // Keep the same function ID so we can remove it later
        newFunc.guid = fn.guid;

        _this3.on(target, type, newFunc);
      })();
    }

    return this;
  };

  /**
   * Trigger an event on an element
   * ```js
   *     myComponent.trigger('eventName');
   *     myComponent.trigger({'type':'eventName'});
   *     myComponent.trigger('eventName', {data: 'some data'});
   *     myComponent.trigger({'type':'eventName'}, {data: 'some data'});
   * ```
   *
   * @param  {Event|Object|String} event  A string (the type) or an event object with a type attribute
   * @param  {Object} [hash] data hash to pass along with the event
   * @return {Component}       self
   * @method trigger
   */

  Component.prototype.trigger = function trigger(event, hash) {
    Events.trigger(this.el_, event, hash);
    return this;
  };

  /**
   * Bind a listener to the component's ready state.
   * Different from event listeners in that if the ready event has already happened
   * it will trigger the function immediately.
   *
   * @param  {Function} fn Ready listener
   * @param  {Boolean} sync Exec the listener synchronously if component is ready
   * @return {Component}
   * @method ready
   */

  Component.prototype.ready = function ready(fn) {
    var sync = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];

    if (fn) {
      if (this.isReady_) {
        if (sync) {
          fn.call(this);
        } else {
          // Call the function asynchronously by default for consistency
          this.setTimeout(fn, 1);
        }
      } else {
        this.readyQueue_ = this.readyQueue_ || [];
        this.readyQueue_.push(fn);
      }
    }
    return this;
  };

  /**
   * Trigger the ready listeners
   *
   * @return {Component}
   * @method triggerReady
   */

  Component.prototype.triggerReady = function triggerReady() {
    this.isReady_ = true;

    // Ensure ready is triggerd asynchronously
    this.setTimeout(function () {
      var readyQueue = this.readyQueue_;

      // Reset Ready Queue
      this.readyQueue_ = [];

      if (readyQueue && readyQueue.length > 0) {
        readyQueue.forEach(function (fn) {
          fn.call(this);
        }, this);
      }

      // Allow for using event listeners also
      this.trigger('ready');
    }, 1);
  };

  /**
   * Finds a single DOM element matching `selector` within the component's
   * `contentEl` or another custom context.
   *
   * @method $
   * @param  {String} selector
   *         A valid CSS selector, which will be passed to `querySelector`.
   *
   * @param  {Element|String} [context=document]
   *         A DOM element within which to query. Can also be a selector
   *         string in which case the first matching element will be used
   *         as context. If missing (or no element matches selector), falls
   *         back to `document`.
   *
   * @return {Element|null}
   */

  Component.prototype.$ = function $(selector, context) {
    return Dom.$(selector, context || this.contentEl());
  };

  /**
   * Finds a all DOM elements matching `selector` within the component's
   * `contentEl` or another custom context.
   *
   * @method $$
   * @param  {String} selector
   *         A valid CSS selector, which will be passed to `querySelectorAll`.
   *
   * @param  {Element|String} [context=document]
   *         A DOM element within which to query. Can also be a selector
   *         string in which case the first matching element will be used
   *         as context. If missing (or no element matches selector), falls
   *         back to `document`.
   *
   * @return {NodeList}
   */

  Component.prototype.$$ = function $$(selector, context) {
    return Dom.$$(selector, context || this.contentEl());
  };

  /**
   * Check if a component's element has a CSS class name
   *
   * @param {String} classToCheck Classname to check
   * @return {Component}
   * @method hasClass
   */

  Component.prototype.hasClass = function hasClass(classToCheck) {
    return Dom.hasElClass(this.el_, classToCheck);
  };

  /**
   * Add a CSS class name to the component's element
   *
   * @param {String} classToAdd Classname to add
   * @return {Component}
   * @method addClass
   */

  Component.prototype.addClass = function addClass(classToAdd) {
    Dom.addElClass(this.el_, classToAdd);
    return this;
  };

  /**
   * Remove a CSS class name from the component's element
   *
   * @param {String} classToRemove Classname to remove
   * @return {Component}
   * @method removeClass
   */

  Component.prototype.removeClass = function removeClass(classToRemove) {
    Dom.removeElClass(this.el_, classToRemove);
    return this;
  };

  /**
   * Add or remove a CSS class name from the component's element
   *
   * @param  {String} classToToggle
   * @param  {Boolean|Function} [predicate]
   *         Can be a function that returns a Boolean. If `true`, the class
   *         will be added; if `false`, the class will be removed. If not
   *         given, the class will be added if not present and vice versa.
   *
   * @return {Component}
   * @method toggleClass
   */

  Component.prototype.toggleClass = function toggleClass(classToToggle, predicate) {
    Dom.toggleElClass(this.el_, classToToggle, predicate);
    return this;
  };

  /**
   * Show the component element if hidden
   *
   * @return {Component}
   * @method show
   */

  Component.prototype.show = function show() {
    this.removeClass('vjs-hidden');
    return this;
  };

  /**
   * Hide the component element if currently showing
   *
   * @return {Component}
   * @method hide
   */

  Component.prototype.hide = function hide() {
    this.addClass('vjs-hidden');
    return this;
  };

  /**
   * Lock an item in its visible state
   * To be used with fadeIn/fadeOut.
   *
   * @return {Component}
   * @private
   * @method lockShowing
   */

  Component.prototype.lockShowing = function lockShowing() {
    this.addClass('vjs-lock-showing');
    return this;
  };

  /**
   * Unlock an item to be hidden
   * To be used with fadeIn/fadeOut.
   *
   * @return {Component}
   * @private
   * @method unlockShowing
   */

  Component.prototype.unlockShowing = function unlockShowing() {
    this.removeClass('vjs-lock-showing');
    return this;
  };

  /**
   * Set or get the width of the component (CSS values)
   * Setting the video tag dimension values only works with values in pixels.
   * Percent values will not work.
   * Some percents can be used, but width()/height() will return the number + %,
   * not the actual computed width/height.
   *
   * @param  {Number|String=} num   Optional width number
   * @param  {Boolean} skipListeners Skip the 'resize' event trigger
   * @return {Component} This component, when setting the width
   * @return {Number|String} The width, when getting
   * @method width
   */

  Component.prototype.width = function width(num, skipListeners) {
    return this.dimension('width', num, skipListeners);
  };

  /**
   * Get or set the height of the component (CSS values)
   * Setting the video tag dimension values only works with values in pixels.
   * Percent values will not work.
   * Some percents can be used, but width()/height() will return the number + %,
   * not the actual computed width/height.
   *
   * @param  {Number|String=} num     New component height
   * @param  {Boolean=} skipListeners Skip the resize event trigger
   * @return {Component} This component, when setting the height
   * @return {Number|String} The height, when getting
   * @method height
   */

  Component.prototype.height = function height(num, skipListeners) {
    return this.dimension('height', num, skipListeners);
  };

  /**
   * Set both width and height at the same time
   *
   * @param  {Number|String} width Width of player
   * @param  {Number|String} height Height of player
   * @return {Component} The component
   * @method dimensions
   */

  Component.prototype.dimensions = function dimensions(width, height) {
    // Skip resize listeners on width for optimization
    return this.width(width, true).height(height);
  };

  /**
   * Get or set width or height
   * This is the shared code for the width() and height() methods.
   * All for an integer, integer + 'px' or integer + '%';
   * Known issue: Hidden elements officially have a width of 0. We're defaulting
   * to the style.width value and falling back to computedStyle which has the
   * hidden element issue. Info, but probably not an efficient fix:
   * http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/
   *
   * @param  {String} widthOrHeight  'width' or 'height'
   * @param  {Number|String=} num     New dimension
   * @param  {Boolean=} skipListeners Skip resize event trigger
   * @return {Component} The component if a dimension was set
   * @return {Number|String} The dimension if nothing was set
   * @private
   * @method dimension
   */

  Component.prototype.dimension = function dimension(widthOrHeight, num, skipListeners) {
    if (num !== undefined) {
      // Set to zero if null or literally NaN (NaN !== NaN)
      if (num === null || num !== num) {
        num = 0;
      }

      // Check if using css width/height (% or px) and adjust
      if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
        this.el_.style[widthOrHeight] = num;
      } else if (num === 'auto') {
        this.el_.style[widthOrHeight] = '';
      } else {
        this.el_.style[widthOrHeight] = num + 'px';
      }

      // skipListeners allows us to avoid triggering the resize event when setting both width and height
      if (!skipListeners) {
        this.trigger('resize');
      }

      // Return component
      return this;
    }

    // Not setting a value, so getting it
    // Make sure element exists
    if (!this.el_) {
      return 0;
    }

    // Get dimension value from style
    var val = this.el_.style[widthOrHeight];
    var pxIndex = val.indexOf('px');

    if (pxIndex !== -1) {
      // Return the pixel value with no 'px'
      return parseInt(val.slice(0, pxIndex), 10);
    }

    // No px so using % or no style was set, so falling back to offsetWidth/height
    // If component has display:none, offset will return 0
    // TODO: handle display:none and no dimension style using px
    return parseInt(this.el_['offset' + _utilsToTitleCaseJs2['default'](widthOrHeight)], 10);
  };

  /**
   * Get width or height of computed style
   * @param  {String} widthOrHeight  'width' or 'height'
   * @return {Number|Boolean} The bolean false if nothing was set
   * @method currentDimension
   */

  Component.prototype.currentDimension = function currentDimension(widthOrHeight) {
    var computedWidthOrHeight = 0;

    if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
      throw new Error('currentDimension only accepts width or height value');
    }

    if (typeof _globalWindow2['default'].getComputedStyle === 'function') {
      var computedStyle = _globalWindow2['default'].getComputedStyle(this.el_);
      computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight];
    } else if (this.el_.currentStyle) {
      // ie 8 doesn't support computed style, shim it
      // return clientWidth or clientHeight instead for better accuracy
      var rule = 'offset' + _utilsToTitleCaseJs2['default'](widthOrHeight);
      computedWidthOrHeight = this.el_[rule];
    }

    // remove 'px' from variable and parse as integer
    computedWidthOrHeight = parseFloat(computedWidthOrHeight);
    return computedWidthOrHeight;
  };

  /**
   * Get an object which contains width and height values of computed style
   * @return {Object} The dimensions of element
   * @method currentDimensions
   */

  Component.prototype.currentDimensions = function currentDimensions() {
    return {
      width: this.currentDimension('width'),
      height: this.currentDimension('height')
    };
  };

  /**
   * Get width of computed style
   * @return {Integer}
   * @method currentWidth
   */

  Component.prototype.currentWidth = function currentWidth() {
    return this.currentDimension('width');
  };

  /**
   * Get height of computed style
   * @return {Integer}
   * @method currentHeight
   */

  Component.prototype.currentHeight = function currentHeight() {
    return this.currentDimension('height');
  };

  /**
   * Emit 'tap' events when touch events are supported
   * This is used to support toggling the controls through a tap on the video.
   * We're requiring them to be enabled because otherwise every component would
   * have this extra overhead unnecessarily, on mobile devices where extra
   * overhead is especially bad.
   *
   * @private
   * @method emitTapEvents
   */

  Component.prototype.emitTapEvents = function emitTapEvents() {
    // Track the start time so we can determine how long the touch lasted
    var touchStart = 0;
    var firstTouch = null;

    // Maximum movement allowed during a touch event to still be considered a tap
    // Other popular libs use anywhere from 2 (hammer.js) to 15, so 10 seems like a nice, round number.
    var tapMovementThreshold = 10;

    // The maximum length a touch can be while still being considered a tap
    var touchTimeThreshold = 200;

    var couldBeTap = undefined;

    this.on('touchstart', function (event) {
      // If more than one finger, don't consider treating this as a click
      if (event.touches.length === 1) {
        // Copy pageX/pageY from the object
        firstTouch = {
          pageX: event.touches[0].pageX,
          pageY: event.touches[0].pageY
        };
        // Record start time so we can detect a tap vs. "touch and hold"
        touchStart = new Date().getTime();
        // Reset couldBeTap tracking
        couldBeTap = true;
      }
    });

    this.on('touchmove', function (event) {
      // If more than one finger, don't consider treating this as a click
      if (event.touches.length > 1) {
        couldBeTap = false;
      } else if (firstTouch) {
        // Some devices will throw touchmoves for all but the slightest of taps.
        // So, if we moved only a small distance, this could still be a tap
        var xdiff = event.touches[0].pageX - firstTouch.pageX;
        var ydiff = event.touches[0].pageY - firstTouch.pageY;
        var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);

        if (touchDistance > tapMovementThreshold) {
          couldBeTap = false;
        }
      }
    });

    var noTap = function noTap() {
      couldBeTap = false;
    };

    // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
    this.on('touchleave', noTap);
    this.on('touchcancel', noTap);

    // When the touch ends, measure how long it took and trigger the appropriate
    // event
    this.on('touchend', function (event) {
      firstTouch = null;
      // Proceed only if the touchmove/leave/cancel event didn't happen
      if (couldBeTap === true && event.cancelable) {
        // Measure how long the touch lasted
        var touchTime = new Date().getTime() - touchStart;

        // Make sure the touch was less than the threshold to be considered a tap
        if (touchTime < touchTimeThreshold) {
          // Don't let browser turn this into a click
          event.preventDefault();
          this.trigger('tap');
          // It may be good to copy the touchend event object and change the
          // type to tap, if the other event properties aren't exact after
          // Events.fixEvent runs (e.g. event.target)
        }
      }
    });
  };

  /**
   * Report user touch activity when touch events occur
   * User activity is used to determine when controls should show/hide. It's
   * relatively simple when it comes to mouse events, because any mouse event
   * should show the controls. So we capture mouse events that bubble up to the
   * player and report activity when that happens.
   * With touch events it isn't as easy. We can't rely on touch events at the
   * player level, because a tap (touchstart + touchend) on the video itself on
   * mobile devices is meant to turn controls off (and on). User activity is
   * checked asynchronously, so what could happen is a tap event on the video
   * turns the controls off, then the touchend event bubbles up to the player,
   * which if it reported user activity, would turn the controls right back on.
   * (We also don't want to completely block touch events from bubbling up)
   * Also a touchmove, touch+hold, and anything other than a tap is not supposed
   * to turn the controls back on on a mobile device.
   * Here we're setting the default component behavior to report user activity
   * whenever touch events happen, and this can be turned off by components that
   * want touch events to act differently.
   *
   * @method enableTouchActivity
   */

  Component.prototype.enableTouchActivity = function enableTouchActivity() {
    // Don't continue if the root player doesn't support reporting user activity
    if (!this.player() || !this.player().reportUserActivity) {
      return;
    }

    // listener for reporting that the user is active
    var report = Fn.bind(this.player(), this.player().reportUserActivity);

    var touchHolding = undefined;

    this.on('touchstart', function () {
      report();
      // For as long as the they are touching the device or have their mouse down,
      // we consider them active even if they're not moving their finger or mouse.
      // So we want to continue to update that they are active
      this.clearInterval(touchHolding);
      // report at the same interval as activityCheck
      touchHolding = this.setInterval(report, 250);
    });

    var touchEnd = function touchEnd(event) {
      report();
      // stop the interval that maintains activity if the touch is holding
      this.clearInterval(touchHolding);
    };

    this.on('touchmove', report);
    this.on('touchend', touchEnd);
    this.on('touchcancel', touchEnd);
  };

  /**
   * Creates timeout and sets up disposal automatically.
   *
   * @param {Function} fn The function to run after the timeout.
   * @param {Number} timeout Number of ms to delay before executing specified function.
   * @return {Number} Returns the timeout ID
   * @method setTimeout
   */

  Component.prototype.setTimeout = function setTimeout(fn, timeout) {
    fn = Fn.bind(this, fn);

    // window.setTimeout would be preferable here, but due to some bizarre issue with Sinon and/or Phantomjs, we can't.
    var timeoutId = _globalWindow2['default'].setTimeout(fn, timeout);

    var disposeFn = function disposeFn() {
      this.clearTimeout(timeoutId);
    };

    disposeFn.guid = 'vjs-timeout-' + timeoutId;

    this.on('dispose', disposeFn);

    return timeoutId;
  };

  /**
   * Clears a timeout and removes the associated dispose listener
   *
   * @param {Number} timeoutId The id of the timeout to clear
   * @return {Number} Returns the timeout ID
   * @method clearTimeout
   */

  Component.prototype.clearTimeout = function clearTimeout(timeoutId) {
    _globalWindow2['default'].clearTimeout(timeoutId);

    var disposeFn = function disposeFn() {};

    disposeFn.guid = 'vjs-timeout-' + timeoutId;

    this.off('dispose', disposeFn);

    return timeoutId;
  };

  /**
   * Creates an interval and sets up disposal automatically.
   *
   * @param {Function} fn The function to run every N seconds.
   * @param {Number} interval Number of ms to delay before executing specified function.
   * @return {Number} Returns the interval ID
   * @method setInterval
   */

  Component.prototype.setInterval = function setInterval(fn, interval) {
    fn = Fn.bind(this, fn);

    var intervalId = _globalWindow2['default'].setInterval(fn, interval);

    var disposeFn = function disposeFn() {
      this.clearInterval(intervalId);
    };

    disposeFn.guid = 'vjs-interval-' + intervalId;

    this.on('dispose', disposeFn);

    return intervalId;
  };

  /**
   * Clears an interval and removes the associated dispose listener
   *
   * @param {Number} intervalId The id of the interval to clear
   * @return {Number} Returns the interval ID
   * @method clearInterval
   */

  Component.prototype.clearInterval = function clearInterval(intervalId) {
    _globalWindow2['default'].clearInterval(intervalId);

    var disposeFn = function disposeFn() {};

    disposeFn.guid = 'vjs-interval-' + intervalId;

    this.off('dispose', disposeFn);

    return intervalId;
  };

  /**
   * Registers a component
   *
   * @param {String} name Name of the component to register
   * @param {Object} comp The component to register
   * @static
   * @method registerComponent
   */

  Component.registerComponent = function registerComponent(name, comp) {
    if (!Component.components_) {
      Component.components_ = {};
    }

    Component.components_[name] = comp;
    return comp;
  };

  /**
   * Gets a component by name
   *
   * @param {String} name Name of the component to get
   * @return {Component}
   * @static
   * @method getComponent
   */

  Component.getComponent = function getComponent(name) {
    if (Component.components_ && Component.components_[name]) {
      return Component.components_[name];
    }

    if (_globalWindow2['default'] && _globalWindow2['default'].videojs && _globalWindow2['default'].videojs[name]) {
      _utilsLogJs2['default'].warn('The ' + name + ' component was added to the videojs object when it should be registered using videojs.registerComponent(name, component)');
      return _globalWindow2['default'].videojs[name];
    }
  };

  /**
   * Sets up the constructor using the supplied init method
   * or uses the init of the parent object
   *
   * @param {Object} props An object of properties
   * @static
   * @deprecated
   * @method extend
   */

  Component.extend = function extend(props) {
    props = props || {};

    _utilsLogJs2['default'].warn('Component.extend({}) has been deprecated, use videojs.extend(Component, {}) instead');

    // Set up the constructor using the supplied init method
    // or using the init of the parent object
    // Make sure to check the unobfuscated version for external libs
    var init = props.init || props.init || this.prototype.init || this.prototype.init || function () {};
    // In Resig's simple class inheritance (previously used) the constructor
    //  is a function that calls `this.init.apply(arguments)`
    // However that would prevent us from using `ParentObject.call(this);`
    //  in a Child constructor because the `this` in `this.init`
    //  would still refer to the Child and cause an infinite loop.
    // We would instead have to do
    //    `ParentObject.prototype.init.apply(this, arguments);`
    //  Bleh. We're not creating a _super() function, so it's good to keep
    //  the parent constructor reference simple.
    var subObj = function subObj() {
      init.apply(this, arguments);
    };

    // Inherit from this object's prototype
    subObj.prototype = Object.create(this.prototype);
    // Reset the constructor property for subObj otherwise
    // instances of subObj would have the constructor of the parent Object
    subObj.prototype.constructor = subObj;

    // Make the class extendable
    subObj.extend = Component.extend;

    // Extend subObj's prototype with functions and other properties from props
    for (var _name in props) {
      if (props.hasOwnProperty(_name)) {
        subObj.prototype[_name] = props[_name];
      }
    }

    return subObj;
  };

  return Component;
})();

Component.registerComponent('Component', Component);
exports['default'] = Component;
module.exports = exports['default'];

},{"./utils/dom.js":144,"./utils/events.js":145,"./utils/fn.js":146,"./utils/guid.js":148,"./utils/log.js":149,"./utils/merge-options.js":150,"./utils/to-title-case.js":153,"global/window":8}],67:[function(_dereq_,module,exports){
/**
 * @file audio-track-button.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _trackButtonJs = _dereq_('../track-button.js');

var _trackButtonJs2 = _interopRequireDefault(_trackButtonJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsFnJs = _dereq_('../../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _audioTrackMenuItemJs = _dereq_('./audio-track-menu-item.js');

var _audioTrackMenuItemJs2 = _interopRequireDefault(_audioTrackMenuItemJs);

/**
 * The base class for buttons that toggle specific text track types (e.g. subtitles)
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends TrackButton
 * @class AudioTrackButton
 */

var AudioTrackButton = (function (_TrackButton) {
  _inherits(AudioTrackButton, _TrackButton);

  function AudioTrackButton(player) {
    var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

    _classCallCheck(this, AudioTrackButton);

    options.tracks = player.audioTracks && player.audioTracks();

    _TrackButton.call(this, player, options);

    this.el_.setAttribute('aria-label', 'Audio Menu');
  }

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  AudioTrackButton.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-audio-button ' + _TrackButton.prototype.buildCSSClass.call(this);
  };

  /**
   * Create a menu item for each audio track
   *
   * @return {Array} Array of menu items
   * @method createItems
   */

  AudioTrackButton.prototype.createItems = function createItems() {
    var items = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];

    var tracks = this.player_.audioTracks && this.player_.audioTracks();

    if (!tracks) {
      return items;
    }

    for (var i = 0; i < tracks.length; i++) {
      var track = tracks[i];

      items.push(new _audioTrackMenuItemJs2['default'](this.player_, {
        // MenuItem is selectable
        'selectable': true,
        'track': track
      }));
    }

    return items;
  };

  return AudioTrackButton;
})(_trackButtonJs2['default']);

_componentJs2['default'].registerComponent('AudioTrackButton', AudioTrackButton);
exports['default'] = AudioTrackButton;
module.exports = exports['default'];

},{"../../component.js":66,"../../utils/fn.js":146,"../track-button.js":97,"./audio-track-menu-item.js":68}],68:[function(_dereq_,module,exports){
/**
 * @file audio-track-menu-item.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _menuMenuItemJs = _dereq_('../../menu/menu-item.js');

var _menuMenuItemJs2 = _interopRequireDefault(_menuMenuItemJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsFnJs = _dereq_('../../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

/**
 * The audio track menu item
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends MenuItem
 * @class AudioTrackMenuItem
 */

var AudioTrackMenuItem = (function (_MenuItem) {
  _inherits(AudioTrackMenuItem, _MenuItem);

  function AudioTrackMenuItem(player, options) {
    var _this = this;

    _classCallCheck(this, AudioTrackMenuItem);

    var track = options.track;
    var tracks = player.audioTracks();

    // Modify options for parent MenuItem class's init.
    options.label = track.label || track.language || 'Unknown';
    options.selected = track.enabled;

    _MenuItem.call(this, player, options);

    this.track = track;

    if (tracks) {
      (function () {
        var changeHandler = Fn.bind(_this, _this.handleTracksChange);

        tracks.addEventListener('change', changeHandler);
        _this.on('dispose', function () {
          tracks.removeEventListener('change', changeHandler);
        });
      })();
    }
  }

  /**
   * Handle click on audio track
   *
   * @method handleClick
   */

  AudioTrackMenuItem.prototype.handleClick = function handleClick(event) {
    var tracks = this.player_.audioTracks();

    _MenuItem.prototype.handleClick.call(this, event);

    if (!tracks) return;

    for (var i = 0; i < tracks.length; i++) {
      var track = tracks[i];

      if (track === this.track) {
        track.enabled = true;
      }
    }
  };

  /**
   * Handle audio track change
   *
   * @method handleTracksChange
   */

  AudioTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
    this.selected(this.track.enabled);
  };

  return AudioTrackMenuItem;
})(_menuMenuItemJs2['default']);

_componentJs2['default'].registerComponent('AudioTrackMenuItem', AudioTrackMenuItem);
exports['default'] = AudioTrackMenuItem;
module.exports = exports['default'];

},{"../../component.js":66,"../../menu/menu-item.js":110,"../../utils/fn.js":146}],69:[function(_dereq_,module,exports){
/**
 * @file control-bar.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

// Required children

var _playToggleJs = _dereq_('./play-toggle.js');

var _playToggleJs2 = _interopRequireDefault(_playToggleJs);

var _timeControlsCurrentTimeDisplayJs = _dereq_('./time-controls/current-time-display.js');

var _timeControlsCurrentTimeDisplayJs2 = _interopRequireDefault(_timeControlsCurrentTimeDisplayJs);

var _timeControlsDurationDisplayJs = _dereq_('./time-controls/duration-display.js');

var _timeControlsDurationDisplayJs2 = _interopRequireDefault(_timeControlsDurationDisplayJs);

var _timeControlsTimeDividerJs = _dereq_('./time-controls/time-divider.js');

var _timeControlsTimeDividerJs2 = _interopRequireDefault(_timeControlsTimeDividerJs);

var _timeControlsRemainingTimeDisplayJs = _dereq_('./time-controls/remaining-time-display.js');

var _timeControlsRemainingTimeDisplayJs2 = _interopRequireDefault(_timeControlsRemainingTimeDisplayJs);

var _liveDisplayJs = _dereq_('./live-display.js');

var _liveDisplayJs2 = _interopRequireDefault(_liveDisplayJs);

var _progressControlProgressControlJs = _dereq_('./progress-control/progress-control.js');

var _progressControlProgressControlJs2 = _interopRequireDefault(_progressControlProgressControlJs);

var _fullscreenToggleJs = _dereq_('./fullscreen-toggle.js');

var _fullscreenToggleJs2 = _interopRequireDefault(_fullscreenToggleJs);

var _volumeControlVolumeControlJs = _dereq_('./volume-control/volume-control.js');

var _volumeControlVolumeControlJs2 = _interopRequireDefault(_volumeControlVolumeControlJs);

var _volumeMenuButtonJs = _dereq_('./volume-menu-button.js');

var _volumeMenuButtonJs2 = _interopRequireDefault(_volumeMenuButtonJs);

var _muteToggleJs = _dereq_('./mute-toggle.js');

var _muteToggleJs2 = _interopRequireDefault(_muteToggleJs);

var _textTrackControlsChaptersButtonJs = _dereq_('./text-track-controls/chapters-button.js');

var _textTrackControlsChaptersButtonJs2 = _interopRequireDefault(_textTrackControlsChaptersButtonJs);

var _textTrackControlsDescriptionsButtonJs = _dereq_('./text-track-controls/descriptions-button.js');

var _textTrackControlsDescriptionsButtonJs2 = _interopRequireDefault(_textTrackControlsDescriptionsButtonJs);

var _textTrackControlsSubtitlesButtonJs = _dereq_('./text-track-controls/subtitles-button.js');

var _textTrackControlsSubtitlesButtonJs2 = _interopRequireDefault(_textTrackControlsSubtitlesButtonJs);

var _textTrackControlsCaptionsButtonJs = _dereq_('./text-track-controls/captions-button.js');

var _textTrackControlsCaptionsButtonJs2 = _interopRequireDefault(_textTrackControlsCaptionsButtonJs);

var _audioTrackControlsAudioTrackButtonJs = _dereq_('./audio-track-controls/audio-track-button.js');

var _audioTrackControlsAudioTrackButtonJs2 = _interopRequireDefault(_audioTrackControlsAudioTrackButtonJs);

var _playbackRateMenuPlaybackRateMenuButtonJs = _dereq_('./playback-rate-menu/playback-rate-menu-button.js');

var _playbackRateMenuPlaybackRateMenuButtonJs2 = _interopRequireDefault(_playbackRateMenuPlaybackRateMenuButtonJs);

var _spacerControlsCustomControlSpacerJs = _dereq_('./spacer-controls/custom-control-spacer.js');

var _spacerControlsCustomControlSpacerJs2 = _interopRequireDefault(_spacerControlsCustomControlSpacerJs);

/**
 * Container of main controls
 *
 * @extends Component
 * @class ControlBar
 */

var ControlBar = (function (_Component) {
  _inherits(ControlBar, _Component);

  function ControlBar() {
    _classCallCheck(this, ControlBar);

    _Component.apply(this, arguments);
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  ControlBar.prototype.createEl = function createEl() {
    return _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-control-bar',
      dir: 'ltr'
    }, {
      'role': 'group' // The control bar is a group, so it can contain menuitems
    });
  };

  return ControlBar;
})(_componentJs2['default']);

ControlBar.prototype.options_ = {
  loadEvent: 'play',
  children: ['playToggle', 'volumeMenuButton', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subtitlesButton', 'captionsButton', 'audioTrackButton', 'fullscreenToggle']
};

_componentJs2['default'].registerComponent('ControlBar', ControlBar);
exports['default'] = ControlBar;
module.exports = exports['default'];

},{"../component.js":66,"./audio-track-controls/audio-track-button.js":67,"./fullscreen-toggle.js":70,"./live-display.js":71,"./mute-toggle.js":72,"./play-toggle.js":73,"./playback-rate-menu/playback-rate-menu-button.js":74,"./progress-control/progress-control.js":79,"./spacer-controls/custom-control-spacer.js":82,"./text-track-controls/captions-button.js":85,"./text-track-controls/chapters-button.js":86,"./text-track-controls/descriptions-button.js":88,"./text-track-controls/subtitles-button.js":90,"./time-controls/current-time-display.js":93,"./time-controls/duration-display.js":94,"./time-controls/remaining-time-display.js":95,"./time-controls/time-divider.js":96,"./volume-control/volume-control.js":99,"./volume-menu-button.js":102}],70:[function(_dereq_,module,exports){
/**
 * @file fullscreen-toggle.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _buttonJs = _dereq_('../button.js');

var _buttonJs2 = _interopRequireDefault(_buttonJs);

var _componentJs = _dereq_('../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

/**
 * Toggle fullscreen video
 *
 * @extends Button
 * @class FullscreenToggle
 */

var FullscreenToggle = (function (_Button) {
  _inherits(FullscreenToggle, _Button);

  function FullscreenToggle(player, options) {
    var _this = this;

    _classCallCheck(this, FullscreenToggle);

    _Button.call(this, player, options);

    player.on('ready', function () {
      _this.player_.on('fullscreenchange', Fn.bind(_this, _this.updateHint));
    });
  }

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  FullscreenToggle.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-fullscreen-control ' + _Button.prototype.buildCSSClass.call(this);
  };

  /**
   * Handles click for full screen
   *
   * @method handleClick
   */

  FullscreenToggle.prototype.handleClick = function handleClick() {
    if (!this.player_.isFullscreen()) {
      this.player_.requestFullscreen();
    } else {
      this.player_.exitFullscreen();
    }

    this.updateHint();
  };

  /**
   * Update button controlText
   *
   * @method update
   */

  FullscreenToggle.prototype.updateHint = function updateHint() {
    if (this.player_.isFullscreen()) {
      this.controlText('Exit fullscreen');
    } else {
      this.controlText('Fullscreen');
    }
  };

  return FullscreenToggle;
})(_buttonJs2['default']);

FullscreenToggle.prototype.controlText_ = 'Fullscreen';

_componentJs2['default'].registerComponent('FullscreenToggle', FullscreenToggle);
exports['default'] = FullscreenToggle;
module.exports = exports['default'];

},{"../button.js":63,"../component.js":66,"../utils/fn.js":146}],71:[function(_dereq_,module,exports){
/**
 * @file live-display.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _component = _dereq_('../component');

var _component2 = _interopRequireDefault(_component);

var _utilsDomJs = _dereq_('../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

/**
 * Displays the live indicator
 * TODO - Future make it click to snap to live
 *
 * @extends Component
 * @class LiveDisplay
 */

var LiveDisplay = (function (_Component) {
  _inherits(LiveDisplay, _Component);

  function LiveDisplay(player, options) {
    _classCallCheck(this, LiveDisplay);

    _Component.call(this, player, options);

    this.updateShowing();
    this.on(this.player(), 'durationchange', this.updateShowing);
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  LiveDisplay.prototype.createEl = function createEl() {
    var el = _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-live-control vjs-control'
    });

    this.contentEl_ = Dom.createEl('div', {
      className: 'vjs-live-display',
      innerHTML: '<span class="vjs-control-text">' + this.localize('Stream Type') + '</span>' + this.localize('LIVE')
    }, {
      'aria-live': 'off'
    });

    el.appendChild(this.contentEl_);
    return el;
  };

  LiveDisplay.prototype.updateShowing = function updateShowing() {
    if (this.player().duration() === Infinity) {
      this.show();
    } else {
      this.hide();
    }
  };

  return LiveDisplay;
})(_component2['default']);

_component2['default'].registerComponent('LiveDisplay', LiveDisplay);
exports['default'] = LiveDisplay;
module.exports = exports['default'];

},{"../component":66,"../utils/dom.js":144}],72:[function(_dereq_,module,exports){
/**
 * @file mute-toggle.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _button = _dereq_('../button');

var _button2 = _interopRequireDefault(_button);

var _component = _dereq_('../component');

var _component2 = _interopRequireDefault(_component);

var _utilsDomJs = _dereq_('../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

/**
 * A button component for muting the audio
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Button
 * @class MuteToggle
 */

var MuteToggle = (function (_Button) {
  _inherits(MuteToggle, _Button);

  function MuteToggle(player, options) {
    _classCallCheck(this, MuteToggle);

    _Button.call(this, player, options);

    this.on(player, 'volumechange', this.update);

    // hide mute toggle if the current tech doesn't support volume control
    if (player.tech_ && player.tech_['featuresMutedControl'] === false) {
      this.addClass('vjs-hidden');
    }

    this.on(player, 'loadstart', function () {
      this.update(); // We need to update the button to account for a default muted state.

      if (player.tech_['featuresMutedControl'] === false) {
        this.addClass('vjs-hidden');
      } else {
        this.removeClass('vjs-hidden');
      }
    });
  }

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  MuteToggle.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-mute-control ' + _Button.prototype.buildCSSClass.call(this);
  };

  /**
   * Handle click on mute
   *
   * @method handleClick
   */

  MuteToggle.prototype.handleClick = function handleClick() {
    this.player_.muted(this.player_.muted() ? false : true);
  };

  /**
   * Update volume
   *
   * @method update
   */

  MuteToggle.prototype.update = function update() {
    var vol = this.player_.volume(),
        level = 3;

    if (vol === 0 || this.player_.muted()) {
      level = 0;
    } else if (vol < 0.33) {
      level = 1;
    } else if (vol < 0.67) {
      level = 2;
    }

    // Don't rewrite the button text if the actual text doesn't change.
    // This causes unnecessary and confusing information for screen reader users.
    // This check is needed because this function gets called every time the volume level is changed.
    var toMute = this.player_.muted() ? 'Unmute' : 'Mute';
    if (this.controlText() !== toMute) {
      this.controlText(toMute);
    }

    /* TODO improve muted icon classes */
    for (var i = 0; i < 4; i++) {
      Dom.removeElClass(this.el_, 'vjs-vol-' + i);
    }
    Dom.addElClass(this.el_, 'vjs-vol-' + level);
  };

  return MuteToggle;
})(_button2['default']);

MuteToggle.prototype.controlText_ = 'Mute';

_component2['default'].registerComponent('MuteToggle', MuteToggle);
exports['default'] = MuteToggle;
module.exports = exports['default'];

},{"../button":63,"../component":66,"../utils/dom.js":144}],73:[function(_dereq_,module,exports){
/**
 * @file play-toggle.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _buttonJs = _dereq_('../button.js');

var _buttonJs2 = _interopRequireDefault(_buttonJs);

var _componentJs = _dereq_('../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

/**
 * Button to toggle between play and pause
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Button
 * @class PlayToggle
 */

var PlayToggle = (function (_Button) {
  _inherits(PlayToggle, _Button);

  function PlayToggle(player, options) {
    _classCallCheck(this, PlayToggle);

    _Button.call(this, player, options);

    this.on(player, 'play', this.handlePlay);
    this.on(player, 'pause', this.handlePause);
  }

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  PlayToggle.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-play-control ' + _Button.prototype.buildCSSClass.call(this);
  };

  /**
   * Handle click to toggle between play and pause
   *
   * @method handleClick
   */

  PlayToggle.prototype.handleClick = function handleClick() {
    if (this.player_.paused()) {
      this.player_.play();
    } else {
      this.player_.pause();
    }
  };

  /**
   * Add the vjs-playing class to the element so it can change appearance
   *
   * @method handlePlay
   */

  PlayToggle.prototype.handlePlay = function handlePlay() {
    this.removeClass('vjs-paused');
    this.addClass('vjs-playing');
    this.controlText('Pause'); // change the button text to "Pause"
  };

  /**
   * Add the vjs-paused class to the element so it can change appearance
   *
   * @method handlePause
   */

  PlayToggle.prototype.handlePause = function handlePause() {
    this.removeClass('vjs-playing');
    this.addClass('vjs-paused');
    this.controlText('Play'); // change the button text to "Play"
  };

  return PlayToggle;
})(_buttonJs2['default']);

PlayToggle.prototype.controlText_ = 'Play';

_componentJs2['default'].registerComponent('PlayToggle', PlayToggle);
exports['default'] = PlayToggle;
module.exports = exports['default'];

},{"../button.js":63,"../component.js":66}],74:[function(_dereq_,module,exports){
/**
 * @file playback-rate-menu-button.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _menuMenuButtonJs = _dereq_('../../menu/menu-button.js');

var _menuMenuButtonJs2 = _interopRequireDefault(_menuMenuButtonJs);

var _menuMenuJs = _dereq_('../../menu/menu.js');

var _menuMenuJs2 = _interopRequireDefault(_menuMenuJs);

var _playbackRateMenuItemJs = _dereq_('./playback-rate-menu-item.js');

var _playbackRateMenuItemJs2 = _interopRequireDefault(_playbackRateMenuItemJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsDomJs = _dereq_('../../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

/**
 * The component for controlling the playback rate
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends MenuButton
 * @class PlaybackRateMenuButton
 */

var PlaybackRateMenuButton = (function (_MenuButton) {
  _inherits(PlaybackRateMenuButton, _MenuButton);

  function PlaybackRateMenuButton(player, options) {
    _classCallCheck(this, PlaybackRateMenuButton);

    _MenuButton.call(this, player, options);

    this.updateVisibility();
    this.updateLabel();

    this.on(player, 'loadstart', this.updateVisibility);
    this.on(player, 'ratechange', this.updateLabel);
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  PlaybackRateMenuButton.prototype.createEl = function createEl() {
    var el = _MenuButton.prototype.createEl.call(this);

    this.labelEl_ = Dom.createEl('div', {
      className: 'vjs-playback-rate-value',
      innerHTML: 1.0
    });

    el.appendChild(this.labelEl_);

    return el;
  };

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  PlaybackRateMenuButton.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-playback-rate ' + _MenuButton.prototype.buildCSSClass.call(this);
  };

  /**
   * Create the playback rate menu
   *
   * @return {Menu} Menu object populated with items
   * @method createMenu
   */

  PlaybackRateMenuButton.prototype.createMenu = function createMenu() {
    var menu = new _menuMenuJs2['default'](this.player());
    var rates = this.playbackRates();

    if (rates) {
      for (var i = rates.length - 1; i >= 0; i--) {
        menu.addChild(new _playbackRateMenuItemJs2['default'](this.player(), { 'rate': rates[i] + 'x' }));
      }
    }

    return menu;
  };

  /**
   * Updates ARIA accessibility attributes
   *
   * @method updateARIAAttributes
   */

  PlaybackRateMenuButton.prototype.updateARIAAttributes = function updateARIAAttributes() {
    // Current playback rate
    this.el().setAttribute('aria-valuenow', this.player().playbackRate());
  };

  /**
   * Handle menu item click
   *
   * @method handleClick
   */

  PlaybackRateMenuButton.prototype.handleClick = function handleClick() {
    // select next rate option
    var currentRate = this.player().playbackRate();
    var rates = this.playbackRates();

    // this will select first one if the last one currently selected
    var newRate = rates[0];
    for (var i = 0; i < rates.length; i++) {
      if (rates[i] > currentRate) {
        newRate = rates[i];
        break;
      }
    }
    this.player().playbackRate(newRate);
  };

  /**
   * Get possible playback rates
   *
   * @return {Array} Possible playback rates
   * @method playbackRates
   */

  PlaybackRateMenuButton.prototype.playbackRates = function playbackRates() {
    return this.options_['playbackRates'] || this.options_.playerOptions && this.options_.playerOptions['playbackRates'];
  };

  /**
   * Get whether playback rates is supported by the tech
   * and an array of playback rates exists
   *
   * @return {Boolean} Whether changing playback rate is supported
   * @method playbackRateSupported
   */

  PlaybackRateMenuButton.prototype.playbackRateSupported = function playbackRateSupported() {
    return this.player().tech_ && this.player().tech_['featuresPlaybackRate'] && this.playbackRates() && this.playbackRates().length > 0;
  };

  /**
   * Hide playback rate controls when they're no playback rate options to select
   *
   * @method updateVisibility
   */

  PlaybackRateMenuButton.prototype.updateVisibility = function updateVisibility() {
    if (this.playbackRateSupported()) {
      this.removeClass('vjs-hidden');
    } else {
      this.addClass('vjs-hidden');
    }
  };

  /**
   * Update button label when rate changed
   *
   * @method updateLabel
   */

  PlaybackRateMenuButton.prototype.updateLabel = function updateLabel() {
    if (this.playbackRateSupported()) {
      this.labelEl_.innerHTML = this.player().playbackRate() + 'x';
    }
  };

  return PlaybackRateMenuButton;
})(_menuMenuButtonJs2['default']);

PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';

_componentJs2['default'].registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);
exports['default'] = PlaybackRateMenuButton;
module.exports = exports['default'];

},{"../../component.js":66,"../../menu/menu-button.js":109,"../../menu/menu.js":111,"../../utils/dom.js":144,"./playback-rate-menu-item.js":75}],75:[function(_dereq_,module,exports){
/**
 * @file playback-rate-menu-item.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _menuMenuItemJs = _dereq_('../../menu/menu-item.js');

var _menuMenuItemJs2 = _interopRequireDefault(_menuMenuItemJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

/**
 * The specific menu item type for selecting a playback rate
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends MenuItem
 * @class PlaybackRateMenuItem
 */

var PlaybackRateMenuItem = (function (_MenuItem) {
  _inherits(PlaybackRateMenuItem, _MenuItem);

  function PlaybackRateMenuItem(player, options) {
    _classCallCheck(this, PlaybackRateMenuItem);

    var label = options['rate'];
    var rate = parseFloat(label, 10);

    // Modify options for parent MenuItem class's init.
    options['label'] = label;
    options['selected'] = rate === 1;
    _MenuItem.call(this, player, options);

    this.label = label;
    this.rate = rate;

    this.on(player, 'ratechange', this.update);
  }

  /**
   * Handle click on menu item
   *
   * @method handleClick
   */

  PlaybackRateMenuItem.prototype.handleClick = function handleClick() {
    _MenuItem.prototype.handleClick.call(this);
    this.player().playbackRate(this.rate);
  };

  /**
   * Update playback rate with selected rate
   *
   * @method update
   */

  PlaybackRateMenuItem.prototype.update = function update() {
    this.selected(this.player().playbackRate() === this.rate);
  };

  return PlaybackRateMenuItem;
})(_menuMenuItemJs2['default']);

PlaybackRateMenuItem.prototype.contentElType = 'button';

_componentJs2['default'].registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);
exports['default'] = PlaybackRateMenuItem;
module.exports = exports['default'];

},{"../../component.js":66,"../../menu/menu-item.js":110}],76:[function(_dereq_,module,exports){
/**
 * @file load-progress-bar.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsDomJs = _dereq_('../../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

/**
 * Shows load progress
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Component
 * @class LoadProgressBar
 */

var LoadProgressBar = (function (_Component) {
  _inherits(LoadProgressBar, _Component);

  function LoadProgressBar(player, options) {
    _classCallCheck(this, LoadProgressBar);

    _Component.call(this, player, options);
    this.on(player, 'progress', this.update);
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  LoadProgressBar.prototype.createEl = function createEl() {
    return _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-load-progress',
      innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Loaded') + '</span>: 0%</span>'
    });
  };

  /**
   * Update progress bar
   *
   * @method update
   */

  LoadProgressBar.prototype.update = function update() {
    var buffered = this.player_.buffered();
    var duration = this.player_.duration();
    var bufferedEnd = this.player_.bufferedEnd();
    var children = this.el_.children;

    // get the percent width of a time compared to the total end
    var percentify = function percentify(time, end) {
      var percent = time / end || 0; // no NaN
      return (percent >= 1 ? 1 : percent) * 100 + '%';
    };

    // update the width of the progress bar
    this.el_.style.width = percentify(bufferedEnd, duration);

    // add child elements to represent the individual buffered time ranges
    for (var i = 0; i < buffered.length; i++) {
      var start = buffered.start(i);
      var end = buffered.end(i);
      var part = children[i];

      if (!part) {
        part = this.el_.appendChild(Dom.createEl());
      }

      // set the percent based on the width of the progress bar (bufferedEnd)
      part.style.left = percentify(start, bufferedEnd);
      part.style.width = percentify(end - start, bufferedEnd);
    }

    // remove unused buffered range elements
    for (var i = children.length; i > buffered.length; i--) {
      this.el_.removeChild(children[i - 1]);
    }
  };

  return LoadProgressBar;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('LoadProgressBar', LoadProgressBar);
exports['default'] = LoadProgressBar;
module.exports = exports['default'];

},{"../../component.js":66,"../../utils/dom.js":144}],77:[function(_dereq_,module,exports){
/**
 * @file mouse-time-display.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsDomJs = _dereq_('../../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFnJs = _dereq_('../../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsFormatTimeJs = _dereq_('../../utils/format-time.js');

var _utilsFormatTimeJs2 = _interopRequireDefault(_utilsFormatTimeJs);

var _lodashCompatFunctionThrottle = _dereq_('lodash-compat/function/throttle');

var _lodashCompatFunctionThrottle2 = _interopRequireDefault(_lodashCompatFunctionThrottle);

/**
 * The Mouse Time Display component shows the time you will seek to
 * when hovering over the progress bar
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Component
 * @class MouseTimeDisplay
 */

var MouseTimeDisplay = (function (_Component) {
  _inherits(MouseTimeDisplay, _Component);

  function MouseTimeDisplay(player, options) {
    var _this = this;

    _classCallCheck(this, MouseTimeDisplay);

    _Component.call(this, player, options);

    if (options.playerOptions && options.playerOptions.controlBar && options.playerOptions.controlBar.progressControl && options.playerOptions.controlBar.progressControl.keepTooltipsInside) {
      this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside;
    }

    if (this.keepTooltipsInside) {
      this.tooltip = Dom.createEl('div', { className: 'vjs-time-tooltip' });
      this.el().appendChild(this.tooltip);
      this.addClass('vjs-keep-tooltips-inside');
    }

    this.update(0, 0, 0);

    player.on('ready', function () {
      var progressEl = _this.player_.controlBar.progressControl.el();
      progressEl.appendChild(_this.tooltip);
      _this.on(progressEl, 'mousemove', _lodashCompatFunctionThrottle2['default'](Fn.bind(_this, _this.handleMouseMove), 25));
    });
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  MouseTimeDisplay.prototype.createEl = function createEl() {
    this.tooltip = Dom.createEl('div', {
      className: 'vjs-mouse-display-tooltip'
    });
    var el = _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-mouse-display'
    });
    return el;
  };

  MouseTimeDisplay.prototype.dispose = function dispose() {
    if (this.tooltip.parentNode) {
      this.tooltip.parentNode.removeChild(this.tooltip);
    }

    Dom.removeElData(this.tooltip);
    this.tooltip = null;

    _Component.prototype.dispose.call(this);
  };

  MouseTimeDisplay.prototype.handleMouseMove = function handleMouseMove(event) {
    var isPercent = false;
    var duration = this.player_.duration();
    if (!duration) {
      isPercent = true;
      duration = 100;
    }
    var newTime = this.calculateDistance(event) * duration;

    var maxLeft = this.player().controlBar.progressControl.seekBar.width() - this.width();
    var position = event.pageX - Dom.findElPosition(this.el().parentNode).left;
    position = Math.min(Math.max(0, position), maxLeft);

    var tooltipWidth = this.tooltip.offsetWidth;
    var maxTooltipLeft = this.tooltip.parentNode.offsetWidth - tooltipWidth;
    var tooltipPosition = event.pageX - Dom.findElPosition(this.tooltip.parentNode).left - tooltipWidth / 2;
    tooltipPosition = Math.min(Math.max(0, tooltipPosition), maxTooltipLeft);

    this.update(newTime, position, tooltipPosition, isPercent);
  };

  MouseTimeDisplay.prototype.update = function update(newTime, position, tooltipPosition, isPercent) {
    var time = isPercent ? Math.round(newTime) + '%' : _utilsFormatTimeJs2['default'](newTime, this.player_.duration());

    this.el().style.left = position + 'px';
    this.tooltip.innerHTML = time;

    if (this.keepTooltipsInside) {
      var clampedPosition = this.clampPosition_(position);
      var difference = position - clampedPosition + 1;
      var tooltipWidth = parseFloat(_globalWindow2['default'].getComputedStyle(this.tooltip).width);
      var tooltipWidthHalf = tooltipWidth / 2;

      this.tooltip.style.left = 'auto';
      this.tooltip.style.right = '-' + (tooltipWidthHalf - difference) + 'px';
    } else {
      this.tooltip.style.right = 'auto';
      this.tooltip.style.left = tooltipPosition + 'px';
    }
  };

  MouseTimeDisplay.prototype.calculateDistance = function calculateDistance(event) {
    return Dom.getPointerPosition(this.el().parentNode, event).x;
  };

  /**
   * This takes in a horizontal position for the bar and returns a clamped position.
   * Clamped position means that it will keep the position greater than half the width
   * of the tooltip and smaller than the player width minus half the width o the tooltip.
   * It will only clamp the position if `keepTooltipsInside` option is set.
   *
   * @param {Number} position the position the bar wants to be
   * @return {Number} newPosition the (potentially) clamped position
   * @method clampPosition_
   */

  MouseTimeDisplay.prototype.clampPosition_ = function clampPosition_(position) {
    if (!this.keepTooltipsInside) {
      return position;
    }

    var playerWidth = parseFloat(_globalWindow2['default'].getComputedStyle(this.player().el()).width);
    var tooltipWidth = parseFloat(_globalWindow2['default'].getComputedStyle(this.tooltip).width);
    var tooltipWidthHalf = tooltipWidth / 2;
    var actualPosition = position;

    if (position < tooltipWidthHalf) {
      actualPosition = Math.ceil(tooltipWidthHalf);
    } else if (position > playerWidth - tooltipWidthHalf) {
      actualPosition = Math.floor(playerWidth - tooltipWidthHalf);
    }

    return actualPosition;
  };

  return MouseTimeDisplay;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('MouseTimeDisplay', MouseTimeDisplay);
exports['default'] = MouseTimeDisplay;
module.exports = exports['default'];

},{"../../component.js":66,"../../utils/dom.js":144,"../../utils/fn.js":146,"../../utils/format-time.js":147,"global/window":8,"lodash-compat/function/throttle":13}],78:[function(_dereq_,module,exports){
/**
 * @file play-progress-bar.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsFnJs = _dereq_('../../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsDomJs = _dereq_('../../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFormatTimeJs = _dereq_('../../utils/format-time.js');

var _utilsFormatTimeJs2 = _interopRequireDefault(_utilsFormatTimeJs);

/**
 * Shows play progress
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Component
 * @class PlayProgressBar
 */

var PlayProgressBar = (function (_Component) {
  _inherits(PlayProgressBar, _Component);

  function PlayProgressBar(player, options) {
    _classCallCheck(this, PlayProgressBar);

    _Component.call(this, player, options);
    this.updateDataAttr();
    this.on(player, 'timeupdate', this.updateDataAttr);
    player.ready(Fn.bind(this, this.updateDataAttr));

    if (options.playerOptions && options.playerOptions.controlBar && options.playerOptions.controlBar.progressControl && options.playerOptions.controlBar.progressControl.keepTooltipsInside) {
      this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside;
    }

    if (this.keepTooltipsInside) {
      this.addClass('vjs-keep-tooltips-inside');
    }
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  PlayProgressBar.prototype.createEl = function createEl() {
    return _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-play-progress vjs-slider-bar',
      innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>'
    });
  };

  PlayProgressBar.prototype.updateDataAttr = function updateDataAttr() {
    var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
    this.el_.setAttribute('data-current-time', _utilsFormatTimeJs2['default'](time, this.player_.duration()));
  };

  return PlayProgressBar;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('PlayProgressBar', PlayProgressBar);
exports['default'] = PlayProgressBar;
module.exports = exports['default'];

},{"../../component.js":66,"../../utils/dom.js":144,"../../utils/fn.js":146,"../../utils/format-time.js":147}],79:[function(_dereq_,module,exports){
/**
 * @file progress-control.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _seekBarJs = _dereq_('./seek-bar.js');

var _seekBarJs2 = _interopRequireDefault(_seekBarJs);

var _mouseTimeDisplayJs = _dereq_('./mouse-time-display.js');

var _mouseTimeDisplayJs2 = _interopRequireDefault(_mouseTimeDisplayJs);

/**
 * The Progress Control component contains the seek bar, load progress,
 * and play progress
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Component
 * @class ProgressControl
 */

var ProgressControl = (function (_Component) {
  _inherits(ProgressControl, _Component);

  function ProgressControl() {
    _classCallCheck(this, ProgressControl);

    _Component.apply(this, arguments);
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  ProgressControl.prototype.createEl = function createEl() {
    return _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-progress-control vjs-control'
    });
  };

  return ProgressControl;
})(_componentJs2['default']);

ProgressControl.prototype.options_ = {
  children: ['seekBar']
};

_componentJs2['default'].registerComponent('ProgressControl', ProgressControl);
exports['default'] = ProgressControl;
module.exports = exports['default'];

},{"../../component.js":66,"./mouse-time-display.js":77,"./seek-bar.js":80}],80:[function(_dereq_,module,exports){
/**
 * @file seek-bar.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _sliderSliderJs = _dereq_('../../slider/slider.js');

var _sliderSliderJs2 = _interopRequireDefault(_sliderSliderJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _loadProgressBarJs = _dereq_('./load-progress-bar.js');

var _loadProgressBarJs2 = _interopRequireDefault(_loadProgressBarJs);

var _playProgressBarJs = _dereq_('./play-progress-bar.js');

var _playProgressBarJs2 = _interopRequireDefault(_playProgressBarJs);

var _tooltipProgressBarJs = _dereq_('./tooltip-progress-bar.js');

var _tooltipProgressBarJs2 = _interopRequireDefault(_tooltipProgressBarJs);

var _utilsFnJs = _dereq_('../../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsBrowserJs = _dereq_('../../utils/browser.js');

var _utilsFormatTimeJs = _dereq_('../../utils/format-time.js');

var _utilsFormatTimeJs2 = _interopRequireDefault(_utilsFormatTimeJs);

var _objectAssign = _dereq_('object.assign');

var _objectAssign2 = _interopRequireDefault(_objectAssign);

/**
 * Seek Bar and holder for the progress bars
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Slider
 * @class SeekBar
 */

var SeekBar = (function (_Slider) {
  _inherits(SeekBar, _Slider);

  function SeekBar(player, options) {
    _classCallCheck(this, SeekBar);

    _Slider.call(this, player, options);
    this.on(player, 'timeupdate', this.updateProgress);
    this.on(player, 'ended', this.updateProgress);
    player.ready(Fn.bind(this, this.updateProgress));

    if (options.playerOptions && options.playerOptions.controlBar && options.playerOptions.controlBar.progressControl && options.playerOptions.controlBar.progressControl.keepTooltipsInside) {
      this.keepTooltipsInside = options.playerOptions.controlBar.progressControl.keepTooltipsInside;
    }

    if (this.keepTooltipsInside) {
      this.tooltipProgressBar = this.addChild('TooltipProgressBar');
    }
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  SeekBar.prototype.createEl = function createEl() {
    return _Slider.prototype.createEl.call(this, 'div', {
      className: 'vjs-progress-holder'
    }, {
      'aria-label': 'progress bar'
    });
  };

  /**
   * Update ARIA accessibility attributes
   *
   * @method updateARIAAttributes
   */

  SeekBar.prototype.updateProgress = function updateProgress() {
    this.updateAriaAttributes(this.el_);

    if (this.keepTooltipsInside) {
      this.updateAriaAttributes(this.tooltipProgressBar.el_);
      this.tooltipProgressBar.el_.style.width = this.bar.el_.style.width;

      var playerWidth = parseFloat(_globalWindow2['default'].getComputedStyle(this.player().el()).width);
      var tooltipWidth = parseFloat(_globalWindow2['default'].getComputedStyle(this.tooltipProgressBar.tooltip).width);
      var tooltipStyle = this.tooltipProgressBar.el().style;
      tooltipStyle.maxWidth = Math.floor(playerWidth - tooltipWidth / 2) + 'px';
      tooltipStyle.minWidth = Math.ceil(tooltipWidth / 2) + 'px';
      tooltipStyle.right = '-' + tooltipWidth / 2 + 'px';
    }
  };

  SeekBar.prototype.updateAriaAttributes = function updateAriaAttributes(el) {
    // Allows for smooth scrubbing, when player can't keep up.
    var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
    el.setAttribute('aria-valuenow', (this.getPercent() * 100).toFixed(2)); // machine readable value of progress bar (percentage complete)
    el.setAttribute('aria-valuetext', _utilsFormatTimeJs2['default'](time, this.player_.duration())); // human readable value of progress bar (time complete)
  };

  /**
   * Get percentage of video played
   *
   * @return {Number} Percentage played
   * @method getPercent
   */

  SeekBar.prototype.getPercent = function getPercent() {
    var percent = this.player_.currentTime() / this.player_.duration();
    return percent >= 1 ? 1 : percent;
  };

  /**
   * Handle mouse down on seek bar
   *
   * @method handleMouseDown
   */

  SeekBar.prototype.handleMouseDown = function handleMouseDown(event) {
    this.player_.scrubbing(true);

    this.videoWasPlaying = !this.player_.paused();
    this.player_.pause();

    _Slider.prototype.handleMouseDown.call(this, event);
  };

  /**
   * Handle mouse move on seek bar
   *
   * @method handleMouseMove
   */

  SeekBar.prototype.handleMouseMove = function handleMouseMove(event) {
    var newTime = this.calculateDistance(event) * this.player_.duration();

    // Don't let video end while scrubbing.
    if (newTime === this.player_.duration()) {
      newTime = newTime - 0.1;
    }

    // Set new time (tell player to seek to new time)
    this.player_.currentTime(newTime);
  };

  /**
   * Handle mouse up on seek bar
   *
   * @method handleMouseUp
   */

  SeekBar.prototype.handleMouseUp = function handleMouseUp(event) {
    _Slider.prototype.handleMouseUp.call(this, event);

    this.player_.scrubbing(false);
    if (this.videoWasPlaying) {
      this.player_.play();
    }
  };

  /**
   * Move more quickly fast forward for keyboard-only users
   *
   * @method stepForward
   */

  SeekBar.prototype.stepForward = function stepForward() {
    this.player_.currentTime(this.player_.currentTime() + 5); // more quickly fast forward for keyboard-only users
  };

  /**
   * Move more quickly rewind for keyboard-only users
   *
   * @method stepBack
   */

  SeekBar.prototype.stepBack = function stepBack() {
    this.player_.currentTime(this.player_.currentTime() - 5); // more quickly rewind for keyboard-only users
  };

  return SeekBar;
})(_sliderSliderJs2['default']);

SeekBar.prototype.options_ = {
  children: ['loadProgressBar', 'playProgressBar'],
  'barName': 'playProgressBar'
};

// MouseTimeDisplay tooltips should not be added to a player on mobile devices or IE8
if ((!_utilsBrowserJs.IE_VERSION || _utilsBrowserJs.IE_VERSION > 8) && !_utilsBrowserJs.IS_IOS && !_utilsBrowserJs.IS_ANDROID) {
  SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');
}

SeekBar.prototype.playerEvent = 'timeupdate';

_componentJs2['default'].registerComponent('SeekBar', SeekBar);
exports['default'] = SeekBar;
module.exports = exports['default'];

},{"../../component.js":66,"../../slider/slider.js":119,"../../utils/browser.js":141,"../../utils/fn.js":146,"../../utils/format-time.js":147,"./load-progress-bar.js":76,"./play-progress-bar.js":78,"./tooltip-progress-bar.js":81,"global/window":8,"object.assign":53}],81:[function(_dereq_,module,exports){
/**
 * @file play-progress-bar.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsFnJs = _dereq_('../../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsDomJs = _dereq_('../../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFormatTimeJs = _dereq_('../../utils/format-time.js');

var _utilsFormatTimeJs2 = _interopRequireDefault(_utilsFormatTimeJs);

/**
 * Shows play progress
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Component
 * @class PlayProgressBar
 */

var TooltipProgressBar = (function (_Component) {
  _inherits(TooltipProgressBar, _Component);

  function TooltipProgressBar(player, options) {
    _classCallCheck(this, TooltipProgressBar);

    _Component.call(this, player, options);
    this.updateDataAttr();
    this.on(player, 'timeupdate', this.updateDataAttr);
    player.ready(Fn.bind(this, this.updateDataAttr));
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  TooltipProgressBar.prototype.createEl = function createEl() {
    var el = _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-tooltip-progress-bar vjs-slider-bar',
      innerHTML: '<div class="vjs-time-tooltip"></div>\n        <span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>'
    });

    this.tooltip = el.querySelector('.vjs-time-tooltip');

    return el;
  };

  TooltipProgressBar.prototype.updateDataAttr = function updateDataAttr() {
    var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
    var formattedTime = _utilsFormatTimeJs2['default'](time, this.player_.duration());
    this.el_.setAttribute('data-current-time', formattedTime);
    this.tooltip.innerHTML = formattedTime;
  };

  return TooltipProgressBar;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('TooltipProgressBar', TooltipProgressBar);
exports['default'] = TooltipProgressBar;
module.exports = exports['default'];

},{"../../component.js":66,"../../utils/dom.js":144,"../../utils/fn.js":146,"../../utils/format-time.js":147}],82:[function(_dereq_,module,exports){
/**
 * @file custom-control-spacer.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _spacerJs = _dereq_('./spacer.js');

var _spacerJs2 = _interopRequireDefault(_spacerJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

/**
 * Spacer specifically meant to be used as an insertion point for new plugins, etc.
 *
 * @extends Spacer
 * @class CustomControlSpacer
 */

var CustomControlSpacer = (function (_Spacer) {
  _inherits(CustomControlSpacer, _Spacer);

  function CustomControlSpacer() {
    _classCallCheck(this, CustomControlSpacer);

    _Spacer.apply(this, arguments);
  }

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  CustomControlSpacer.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-custom-control-spacer ' + _Spacer.prototype.buildCSSClass.call(this);
  };

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  CustomControlSpacer.prototype.createEl = function createEl() {
    var el = _Spacer.prototype.createEl.call(this, {
      className: this.buildCSSClass()
    });

    // No-flex/table-cell mode requires there be some content
    // in the cell to fill the remaining space of the table.
    el.innerHTML = '&nbsp;';
    return el;
  };

  return CustomControlSpacer;
})(_spacerJs2['default']);

_componentJs2['default'].registerComponent('CustomControlSpacer', CustomControlSpacer);
exports['default'] = CustomControlSpacer;
module.exports = exports['default'];

},{"../../component.js":66,"./spacer.js":83}],83:[function(_dereq_,module,exports){
/**
 * @file spacer.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

/**
 * Just an empty spacer element that can be used as an append point for plugins, etc.
 * Also can be used to create space between elements when necessary.
 *
 * @extends Component
 * @class Spacer
 */

var Spacer = (function (_Component) {
  _inherits(Spacer, _Component);

  function Spacer() {
    _classCallCheck(this, Spacer);

    _Component.apply(this, arguments);
  }

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  Spacer.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-spacer ' + _Component.prototype.buildCSSClass.call(this);
  };

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  Spacer.prototype.createEl = function createEl() {
    return _Component.prototype.createEl.call(this, 'div', {
      className: this.buildCSSClass()
    });
  };

  return Spacer;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('Spacer', Spacer);

exports['default'] = Spacer;
module.exports = exports['default'];

},{"../../component.js":66}],84:[function(_dereq_,module,exports){
/**
 * @file caption-settings-menu-item.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _textTrackMenuItemJs = _dereq_('./text-track-menu-item.js');

var _textTrackMenuItemJs2 = _interopRequireDefault(_textTrackMenuItemJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

/**
 * The menu item for caption track settings menu
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends TextTrackMenuItem
 * @class CaptionSettingsMenuItem
 */

var CaptionSettingsMenuItem = (function (_TextTrackMenuItem) {
  _inherits(CaptionSettingsMenuItem, _TextTrackMenuItem);

  function CaptionSettingsMenuItem(player, options) {
    _classCallCheck(this, CaptionSettingsMenuItem);

    options['track'] = {
      'kind': options['kind'],
      'player': player,
      'label': options['kind'] + ' settings',
      'selectable': false,
      'default': false,
      mode: 'disabled'
    };

    // CaptionSettingsMenuItem has no concept of 'selected'
    options['selectable'] = false;

    _TextTrackMenuItem.call(this, player, options);
    this.addClass('vjs-texttrack-settings');
    this.controlText(', opens ' + options['kind'] + ' settings dialog');
  }

  /**
   * Handle click on menu item
   *
   * @method handleClick
   */

  CaptionSettingsMenuItem.prototype.handleClick = function handleClick() {
    this.player().getChild('textTrackSettings').show();
    this.player().getChild('textTrackSettings').el_.focus();
  };

  return CaptionSettingsMenuItem;
})(_textTrackMenuItemJs2['default']);

_componentJs2['default'].registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem);
exports['default'] = CaptionSettingsMenuItem;
module.exports = exports['default'];

},{"../../component.js":66,"./text-track-menu-item.js":92}],85:[function(_dereq_,module,exports){
/**
 * @file captions-button.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _textTrackButtonJs = _dereq_('./text-track-button.js');

var _textTrackButtonJs2 = _interopRequireDefault(_textTrackButtonJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _captionSettingsMenuItemJs = _dereq_('./caption-settings-menu-item.js');

var _captionSettingsMenuItemJs2 = _interopRequireDefault(_captionSettingsMenuItemJs);

/**
 * The button component for toggling and selecting captions
 *
 * @param {Object} player  Player object
 * @param {Object=} options Object of option names and values
 * @param {Function=} ready    Ready callback function
 * @extends TextTrackButton
 * @class CaptionsButton
 */

var CaptionsButton = (function (_TextTrackButton) {
  _inherits(CaptionsButton, _TextTrackButton);

  function CaptionsButton(player, options, ready) {
    _classCallCheck(this, CaptionsButton);

    _TextTrackButton.call(this, player, options, ready);
    this.el_.setAttribute('aria-label', 'Captions Menu');
  }

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  CaptionsButton.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-captions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  };

  /**
   * Update caption menu items
   *
   * @method update
   */

  CaptionsButton.prototype.update = function update() {
    var threshold = 2;
    _TextTrackButton.prototype.update.call(this);

    // if native, then threshold is 1 because no settings button
    if (this.player().tech_ && this.player().tech_['featuresNativeTextTracks']) {
      threshold = 1;
    }

    if (this.items && this.items.length > threshold) {
      this.show();
    } else {
      this.hide();
    }
  };

  /**
   * Create caption menu items
   *
   * @return {Array} Array of menu items
   * @method createItems
   */

  CaptionsButton.prototype.createItems = function createItems() {
    var items = [];

    if (!(this.player().tech_ && this.player().tech_['featuresNativeTextTracks'])) {
      items.push(new _captionSettingsMenuItemJs2['default'](this.player_, { 'kind': this.kind_ }));
    }

    return _TextTrackButton.prototype.createItems.call(this, items);
  };

  return CaptionsButton;
})(_textTrackButtonJs2['default']);

CaptionsButton.prototype.kind_ = 'captions';
CaptionsButton.prototype.controlText_ = 'Captions';

_componentJs2['default'].registerComponent('CaptionsButton', CaptionsButton);
exports['default'] = CaptionsButton;
module.exports = exports['default'];

},{"../../component.js":66,"./caption-settings-menu-item.js":84,"./text-track-button.js":91}],86:[function(_dereq_,module,exports){
/**
 * @file chapters-button.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _textTrackButtonJs = _dereq_('./text-track-button.js');

var _textTrackButtonJs2 = _interopRequireDefault(_textTrackButtonJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _textTrackMenuItemJs = _dereq_('./text-track-menu-item.js');

var _textTrackMenuItemJs2 = _interopRequireDefault(_textTrackMenuItemJs);

var _chaptersTrackMenuItemJs = _dereq_('./chapters-track-menu-item.js');

var _chaptersTrackMenuItemJs2 = _interopRequireDefault(_chaptersTrackMenuItemJs);

var _menuMenuJs = _dereq_('../../menu/menu.js');

var _menuMenuJs2 = _interopRequireDefault(_menuMenuJs);

var _utilsDomJs = _dereq_('../../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFnJs = _dereq_('../../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsToTitleCaseJs = _dereq_('../../utils/to-title-case.js');

var _utilsToTitleCaseJs2 = _interopRequireDefault(_utilsToTitleCaseJs);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

/**
 * The button component for toggling and selecting chapters
 * Chapters act much differently than other text tracks
 * Cues are navigation vs. other tracks of alternative languages
 *
 * @param {Object} player  Player object
 * @param {Object=} options Object of option names and values
 * @param {Function=} ready    Ready callback function
 * @extends TextTrackButton
 * @class ChaptersButton
 */

var ChaptersButton = (function (_TextTrackButton) {
  _inherits(ChaptersButton, _TextTrackButton);

  function ChaptersButton(player, options, ready) {
    _classCallCheck(this, ChaptersButton);

    _TextTrackButton.call(this, player, options, ready);
    this.el_.setAttribute('aria-label', 'Chapters Menu');
  }

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  ChaptersButton.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  };

  /**
   * Create a menu item for each text track
   *
   * @return {Array} Array of menu items
   * @method createItems
   */

  ChaptersButton.prototype.createItems = function createItems() {
    var items = [];

    var tracks = this.player_.textTracks();

    if (!tracks) {
      return items;
    }

    for (var i = 0; i < tracks.length; i++) {
      var track = tracks[i];
      if (track['kind'] === this.kind_) {
        items.push(new _textTrackMenuItemJs2['default'](this.player_, {
          'track': track
        }));
      }
    }

    return items;
  };

  /**
   * Create menu from chapter buttons
   *
   * @return {Menu} Menu of chapter buttons
   * @method createMenu
   */

  ChaptersButton.prototype.createMenu = function createMenu() {
    var _this = this;

    var tracks = this.player_.textTracks() || [];
    var chaptersTrack = undefined;
    var items = this.items = [];

    for (var i = 0, _length = tracks.length; i < _length; i++) {
      var track = tracks[i];

      if (track['kind'] === this.kind_) {
        chaptersTrack = track;

        break;
      }
    }

    var menu = this.menu;
    if (menu === undefined) {
      menu = new _menuMenuJs2['default'](this.player_);
      var title = Dom.createEl('li', {
        className: 'vjs-menu-title',
        innerHTML: _utilsToTitleCaseJs2['default'](this.kind_),
        tabIndex: -1
      });
      menu.children_.unshift(title);
      Dom.insertElFirst(title, menu.contentEl());
    }

    if (chaptersTrack && chaptersTrack.cues == null) {
      chaptersTrack['mode'] = 'hidden';

      var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(chaptersTrack);

      if (remoteTextTrackEl) {
        remoteTextTrackEl.addEventListener('load', function (event) {
          return _this.update();
        });
      }
    }

    if (chaptersTrack && chaptersTrack.cues && chaptersTrack.cues.length > 0) {
      var cues = chaptersTrack['cues'],
          cue = undefined;

      for (var i = 0, l = cues.length; i < l; i++) {
        cue = cues[i];

        var mi = new _chaptersTrackMenuItemJs2['default'](this.player_, {
          'track': chaptersTrack,
          'cue': cue
        });

        items.push(mi);

        menu.addChild(mi);
      }

      this.addChild(menu);
    }

    if (this.items.length > 0) {
      this.show();
    }

    return menu;
  };

  return ChaptersButton;
})(_textTrackButtonJs2['default']);

ChaptersButton.prototype.kind_ = 'chapters';
ChaptersButton.prototype.controlText_ = 'Chapters';

_componentJs2['default'].registerComponent('ChaptersButton', ChaptersButton);
exports['default'] = ChaptersButton;
module.exports = exports['default'];

},{"../../component.js":66,"../../menu/menu.js":111,"../../utils/dom.js":144,"../../utils/fn.js":146,"../../utils/to-title-case.js":153,"./chapters-track-menu-item.js":87,"./text-track-button.js":91,"./text-track-menu-item.js":92,"global/window":8}],87:[function(_dereq_,module,exports){
/**
 * @file chapters-track-menu-item.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _menuMenuItemJs = _dereq_('../../menu/menu-item.js');

var _menuMenuItemJs2 = _interopRequireDefault(_menuMenuItemJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsFnJs = _dereq_('../../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

/**
 * The chapter track menu item
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends MenuItem
 * @class ChaptersTrackMenuItem
 */

var ChaptersTrackMenuItem = (function (_MenuItem) {
  _inherits(ChaptersTrackMenuItem, _MenuItem);

  function ChaptersTrackMenuItem(player, options) {
    _classCallCheck(this, ChaptersTrackMenuItem);

    var track = options['track'];
    var cue = options['cue'];
    var currentTime = player.currentTime();

    // Modify options for parent MenuItem class's init.
    options['label'] = cue.text;
    options['selected'] = cue['startTime'] <= currentTime && currentTime < cue['endTime'];
    _MenuItem.call(this, player, options);

    this.track = track;
    this.cue = cue;
    track.addEventListener('cuechange', Fn.bind(this, this.update));
  }

  /**
   * Handle click on menu item
   *
   * @method handleClick
   */

  ChaptersTrackMenuItem.prototype.handleClick = function handleClick() {
    _MenuItem.prototype.handleClick.call(this);
    this.player_.currentTime(this.cue.startTime);
    this.update(this.cue.startTime);
  };

  /**
   * Update chapter menu item
   *
   * @method update
   */

  ChaptersTrackMenuItem.prototype.update = function update() {
    var cue = this.cue;
    var currentTime = this.player_.currentTime();

    // vjs.log(currentTime, cue.startTime);
    this.selected(cue['startTime'] <= currentTime && currentTime < cue['endTime']);
  };

  return ChaptersTrackMenuItem;
})(_menuMenuItemJs2['default']);

_componentJs2['default'].registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem);
exports['default'] = ChaptersTrackMenuItem;
module.exports = exports['default'];

},{"../../component.js":66,"../../menu/menu-item.js":110,"../../utils/fn.js":146}],88:[function(_dereq_,module,exports){
/**
 * @file descriptions-button.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _textTrackButtonJs = _dereq_('./text-track-button.js');

var _textTrackButtonJs2 = _interopRequireDefault(_textTrackButtonJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsFnJs = _dereq_('../../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

/**
 * The button component for toggling and selecting descriptions
 *
 * @param {Object} player  Player object
 * @param {Object=} options Object of option names and values
 * @param {Function=} ready    Ready callback function
 * @extends TextTrackButton
 * @class DescriptionsButton
 */

var DescriptionsButton = (function (_TextTrackButton) {
  _inherits(DescriptionsButton, _TextTrackButton);

  function DescriptionsButton(player, options, ready) {
    var _this = this;

    _classCallCheck(this, DescriptionsButton);

    _TextTrackButton.call(this, player, options, ready);
    this.el_.setAttribute('aria-label', 'Descriptions Menu');

    var tracks = player.textTracks();

    if (tracks) {
      (function () {
        var changeHandler = Fn.bind(_this, _this.handleTracksChange);

        tracks.addEventListener('change', changeHandler);
        _this.on('dispose', function () {
          tracks.removeEventListener('change', changeHandler);
        });
      })();
    }
  }

  /**
   * Handle text track change
   *
   * @method handleTracksChange
   */

  DescriptionsButton.prototype.handleTracksChange = function handleTracksChange(event) {
    var tracks = this.player().textTracks();
    var disabled = false;

    // Check whether a track of a different kind is showing
    for (var i = 0, l = tracks.length; i < l; i++) {
      var track = tracks[i];
      if (track['kind'] !== this.kind_ && track['mode'] === 'showing') {
        disabled = true;
        break;
      }
    }

    // If another track is showing, disable this menu button
    if (disabled) {
      this.disable();
    } else {
      this.enable();
    }
  };

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  DescriptionsButton.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  };

  return DescriptionsButton;
})(_textTrackButtonJs2['default']);

DescriptionsButton.prototype.kind_ = 'descriptions';
DescriptionsButton.prototype.controlText_ = 'Descriptions';

_componentJs2['default'].registerComponent('DescriptionsButton', DescriptionsButton);
exports['default'] = DescriptionsButton;
module.exports = exports['default'];

},{"../../component.js":66,"../../utils/fn.js":146,"./text-track-button.js":91}],89:[function(_dereq_,module,exports){
/**
 * @file off-text-track-menu-item.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _textTrackMenuItemJs = _dereq_('./text-track-menu-item.js');

var _textTrackMenuItemJs2 = _interopRequireDefault(_textTrackMenuItemJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

/**
 * A special menu item for turning of a specific type of text track
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends TextTrackMenuItem
 * @class OffTextTrackMenuItem
 */

var OffTextTrackMenuItem = (function (_TextTrackMenuItem) {
  _inherits(OffTextTrackMenuItem, _TextTrackMenuItem);

  function OffTextTrackMenuItem(player, options) {
    _classCallCheck(this, OffTextTrackMenuItem);

    // Create pseudo track info
    // Requires options['kind']
    options['track'] = {
      'kind': options['kind'],
      'player': player,
      'label': options['kind'] + ' off',
      'default': false,
      'mode': 'disabled'
    };

    // MenuItem is selectable
    options['selectable'] = true;

    _TextTrackMenuItem.call(this, player, options);
    this.selected(true);
  }

  /**
   * Handle text track change
   *
   * @param {Object} event Event object
   * @method handleTracksChange
   */

  OffTextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
    var tracks = this.player().textTracks();
    var selected = true;

    for (var i = 0, l = tracks.length; i < l; i++) {
      var track = tracks[i];
      if (track['kind'] === this.track['kind'] && track['mode'] === 'showing') {
        selected = false;
        break;
      }
    }

    this.selected(selected);
  };

  return OffTextTrackMenuItem;
})(_textTrackMenuItemJs2['default']);

_componentJs2['default'].registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem);
exports['default'] = OffTextTrackMenuItem;
module.exports = exports['default'];

},{"../../component.js":66,"./text-track-menu-item.js":92}],90:[function(_dereq_,module,exports){
/**
 * @file subtitles-button.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _textTrackButtonJs = _dereq_('./text-track-button.js');

var _textTrackButtonJs2 = _interopRequireDefault(_textTrackButtonJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

/**
 * The button component for toggling and selecting subtitles
 *
 * @param {Object} player  Player object
 * @param {Object=} options Object of option names and values
 * @param {Function=} ready    Ready callback function
 * @extends TextTrackButton
 * @class SubtitlesButton
 */

var SubtitlesButton = (function (_TextTrackButton) {
  _inherits(SubtitlesButton, _TextTrackButton);

  function SubtitlesButton(player, options, ready) {
    _classCallCheck(this, SubtitlesButton);

    _TextTrackButton.call(this, player, options, ready);
    this.el_.setAttribute('aria-label', 'Subtitles Menu');
  }

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  SubtitlesButton.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
  };

  return SubtitlesButton;
})(_textTrackButtonJs2['default']);

SubtitlesButton.prototype.kind_ = 'subtitles';
SubtitlesButton.prototype.controlText_ = 'Subtitles';

_componentJs2['default'].registerComponent('SubtitlesButton', SubtitlesButton);
exports['default'] = SubtitlesButton;
module.exports = exports['default'];

},{"../../component.js":66,"./text-track-button.js":91}],91:[function(_dereq_,module,exports){
/**
 * @file text-track-button.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _trackButtonJs = _dereq_('../track-button.js');

var _trackButtonJs2 = _interopRequireDefault(_trackButtonJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsFnJs = _dereq_('../../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _textTrackMenuItemJs = _dereq_('./text-track-menu-item.js');

var _textTrackMenuItemJs2 = _interopRequireDefault(_textTrackMenuItemJs);

var _offTextTrackMenuItemJs = _dereq_('./off-text-track-menu-item.js');

var _offTextTrackMenuItemJs2 = _interopRequireDefault(_offTextTrackMenuItemJs);

/**
 * The base class for buttons that toggle specific text track types (e.g. subtitles)
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends MenuButton
 * @class TextTrackButton
 */

var TextTrackButton = (function (_TrackButton) {
  _inherits(TextTrackButton, _TrackButton);

  function TextTrackButton(player) {
    var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

    _classCallCheck(this, TextTrackButton);

    options.tracks = player.textTracks();

    _TrackButton.call(this, player, options);
  }

  /**
   * Create a menu item for each text track
   *
   * @return {Array} Array of menu items
   * @method createItems
   */

  TextTrackButton.prototype.createItems = function createItems() {
    var items = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];

    // Add an OFF menu item to turn all tracks off
    items.push(new _offTextTrackMenuItemJs2['default'](this.player_, { 'kind': this.kind_ }));

    var tracks = this.player_.textTracks();

    if (!tracks) {
      return items;
    }

    for (var i = 0; i < tracks.length; i++) {
      var track = tracks[i];

      // only add tracks that are of the appropriate kind and have a label
      if (track['kind'] === this.kind_) {
        items.push(new _textTrackMenuItemJs2['default'](this.player_, {
          // MenuItem is selectable
          'selectable': true,
          'track': track
        }));
      }
    }

    return items;
  };

  return TextTrackButton;
})(_trackButtonJs2['default']);

_componentJs2['default'].registerComponent('TextTrackButton', TextTrackButton);
exports['default'] = TextTrackButton;
module.exports = exports['default'];

},{"../../component.js":66,"../../utils/fn.js":146,"../track-button.js":97,"./off-text-track-menu-item.js":89,"./text-track-menu-item.js":92}],92:[function(_dereq_,module,exports){
/**
 * @file text-track-menu-item.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _menuMenuItemJs = _dereq_('../../menu/menu-item.js');

var _menuMenuItemJs2 = _interopRequireDefault(_menuMenuItemJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsFnJs = _dereq_('../../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

/**
 * The specific menu item type for selecting a language within a text track kind
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends MenuItem
 * @class TextTrackMenuItem
 */

var TextTrackMenuItem = (function (_MenuItem) {
  _inherits(TextTrackMenuItem, _MenuItem);

  function TextTrackMenuItem(player, options) {
    var _this = this;

    _classCallCheck(this, TextTrackMenuItem);

    var track = options['track'];
    var tracks = player.textTracks();

    // Modify options for parent MenuItem class's init.
    options['label'] = track['label'] || track['language'] || 'Unknown';
    options['selected'] = track['default'] || track['mode'] === 'showing';

    _MenuItem.call(this, player, options);

    this.track = track;

    if (tracks) {
      (function () {
        var changeHandler = Fn.bind(_this, _this.handleTracksChange);

        tracks.addEventListener('change', changeHandler);
        _this.on('dispose', function () {
          tracks.removeEventListener('change', changeHandler);
        });
      })();
    }

    // iOS7 doesn't dispatch change events to TextTrackLists when an
    // associated track's mode changes. Without something like
    // Object.observe() (also not present on iOS7), it's not
    // possible to detect changes to the mode attribute and polyfill
    // the change event. As a poor substitute, we manually dispatch
    // change events whenever the controls modify the mode.
    if (tracks && tracks.onchange === undefined) {
      (function () {
        var event = undefined;

        _this.on(['tap', 'click'], function () {
          if (typeof _globalWindow2['default'].Event !== 'object') {
            // Android 2.3 throws an Illegal Constructor error for window.Event
            try {
              event = new _globalWindow2['default'].Event('change');
            } catch (err) {}
          }

          if (!event) {
            event = _globalDocument2['default'].createEvent('Event');
            event.initEvent('change', true, true);
          }

          tracks.dispatchEvent(event);
        });
      })();
    }
  }

  /**
   * Handle click on text track
   *
   * @method handleClick
   */

  TextTrackMenuItem.prototype.handleClick = function handleClick(event) {
    var kind = this.track['kind'];
    var tracks = this.player_.textTracks();

    _MenuItem.prototype.handleClick.call(this, event);

    if (!tracks) return;

    for (var i = 0; i < tracks.length; i++) {
      var track = tracks[i];

      if (track['kind'] !== kind) {
        continue;
      }

      if (track === this.track) {
        track['mode'] = 'showing';
      } else {
        track['mode'] = 'disabled';
      }
    }
  };

  /**
   * Handle text track change
   *
   * @method handleTracksChange
   */

  TextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
    this.selected(this.track['mode'] === 'showing');
  };

  return TextTrackMenuItem;
})(_menuMenuItemJs2['default']);

_componentJs2['default'].registerComponent('TextTrackMenuItem', TextTrackMenuItem);
exports['default'] = TextTrackMenuItem;
module.exports = exports['default'];

},{"../../component.js":66,"../../menu/menu-item.js":110,"../../utils/fn.js":146,"global/document":7,"global/window":8}],93:[function(_dereq_,module,exports){
/**
 * @file current-time-display.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsDomJs = _dereq_('../../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFormatTimeJs = _dereq_('../../utils/format-time.js');

var _utilsFormatTimeJs2 = _interopRequireDefault(_utilsFormatTimeJs);

/**
 * Displays the current time
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Component
 * @class CurrentTimeDisplay
 */

var CurrentTimeDisplay = (function (_Component) {
  _inherits(CurrentTimeDisplay, _Component);

  function CurrentTimeDisplay(player, options) {
    _classCallCheck(this, CurrentTimeDisplay);

    _Component.call(this, player, options);

    this.on(player, 'timeupdate', this.updateContent);
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  CurrentTimeDisplay.prototype.createEl = function createEl() {
    var el = _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-current-time vjs-time-control vjs-control'
    });

    this.contentEl_ = Dom.createEl('div', {
      className: 'vjs-current-time-display',
      // label the current time for screen reader users
      innerHTML: '<span class="vjs-control-text">Current Time </span>' + '0:00'
    }, {
      // tell screen readers not to automatically read the time as it changes
      'aria-live': 'off'
    });

    el.appendChild(this.contentEl_);
    return el;
  };

  /**
   * Update current time display
   *
   * @method updateContent
   */

  CurrentTimeDisplay.prototype.updateContent = function updateContent() {
    // Allows for smooth scrubbing, when player can't keep up.
    var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
    var localizedText = this.localize('Current Time');
    var formattedTime = _utilsFormatTimeJs2['default'](time, this.player_.duration());
    if (formattedTime !== this.formattedTime_) {
      this.formattedTime_ = formattedTime;
      this.contentEl_.innerHTML = '<span class="vjs-control-text">' + localizedText + '</span> ' + formattedTime;
    }
  };

  return CurrentTimeDisplay;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('CurrentTimeDisplay', CurrentTimeDisplay);
exports['default'] = CurrentTimeDisplay;
module.exports = exports['default'];

},{"../../component.js":66,"../../utils/dom.js":144,"../../utils/format-time.js":147}],94:[function(_dereq_,module,exports){
/**
 * @file duration-display.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsDomJs = _dereq_('../../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFormatTimeJs = _dereq_('../../utils/format-time.js');

var _utilsFormatTimeJs2 = _interopRequireDefault(_utilsFormatTimeJs);

/**
 * Displays the duration
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Component
 * @class DurationDisplay
 */

var DurationDisplay = (function (_Component) {
  _inherits(DurationDisplay, _Component);

  function DurationDisplay(player, options) {
    _classCallCheck(this, DurationDisplay);

    _Component.call(this, player, options);

    // this might need to be changed to 'durationchange' instead of 'timeupdate' eventually,
    // however the durationchange event fires before this.player_.duration() is set,
    // so the value cannot be written out using this method.
    // Once the order of durationchange and this.player_.duration() being set is figured out,
    // this can be updated.
    this.on(player, 'timeupdate', this.updateContent);
    this.on(player, 'loadedmetadata', this.updateContent);
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  DurationDisplay.prototype.createEl = function createEl() {
    var el = _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-duration vjs-time-control vjs-control'
    });

    this.contentEl_ = Dom.createEl('div', {
      className: 'vjs-duration-display',
      // label the duration time for screen reader users
      innerHTML: '<span class="vjs-control-text">' + this.localize('Duration Time') + '</span> 0:00'
    }, {
      // tell screen readers not to automatically read the time as it changes
      'aria-live': 'off'
    });

    el.appendChild(this.contentEl_);
    return el;
  };

  /**
   * Update duration time display
   *
   * @method updateContent
   */

  DurationDisplay.prototype.updateContent = function updateContent() {
    var duration = this.player_.duration();
    if (duration && this.duration_ !== duration) {
      this.duration_ = duration;
      var localizedText = this.localize('Duration Time');
      var formattedTime = _utilsFormatTimeJs2['default'](duration);
      this.contentEl_.innerHTML = '<span class="vjs-control-text">' + localizedText + '</span> ' + formattedTime; // label the duration time for screen reader users
    }
  };

  return DurationDisplay;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('DurationDisplay', DurationDisplay);
exports['default'] = DurationDisplay;
module.exports = exports['default'];

},{"../../component.js":66,"../../utils/dom.js":144,"../../utils/format-time.js":147}],95:[function(_dereq_,module,exports){
/**
 * @file remaining-time-display.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsDomJs = _dereq_('../../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFormatTimeJs = _dereq_('../../utils/format-time.js');

var _utilsFormatTimeJs2 = _interopRequireDefault(_utilsFormatTimeJs);

/**
 * Displays the time left in the video
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Component
 * @class RemainingTimeDisplay
 */

var RemainingTimeDisplay = (function (_Component) {
  _inherits(RemainingTimeDisplay, _Component);

  function RemainingTimeDisplay(player, options) {
    _classCallCheck(this, RemainingTimeDisplay);

    _Component.call(this, player, options);

    this.on(player, 'timeupdate', this.updateContent);
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  RemainingTimeDisplay.prototype.createEl = function createEl() {
    var el = _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-remaining-time vjs-time-control vjs-control'
    });

    this.contentEl_ = Dom.createEl('div', {
      className: 'vjs-remaining-time-display',
      // label the remaining time for screen reader users
      innerHTML: '<span class="vjs-control-text">' + this.localize('Remaining Time') + '</span> -0:00'
    }, {
      // tell screen readers not to automatically read the time as it changes
      'aria-live': 'off'
    });

    el.appendChild(this.contentEl_);
    return el;
  };

  /**
   * Update remaining time display
   *
   * @method updateContent
   */

  RemainingTimeDisplay.prototype.updateContent = function updateContent() {
    if (this.player_.duration()) {
      var localizedText = this.localize('Remaining Time');
      var formattedTime = _utilsFormatTimeJs2['default'](this.player_.remainingTime());
      if (formattedTime !== this.formattedTime_) {
        this.formattedTime_ = formattedTime;
        this.contentEl_.innerHTML = '<span class="vjs-control-text">' + localizedText + '</span> -' + formattedTime;
      }
    }

    // Allows for smooth scrubbing, when player can't keep up.
    // var time = (this.player_.scrubbing()) ? this.player_.getCache().currentTime : this.player_.currentTime();
    // this.contentEl_.innerHTML = vjs.formatTime(time, this.player_.duration());
  };

  return RemainingTimeDisplay;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('RemainingTimeDisplay', RemainingTimeDisplay);
exports['default'] = RemainingTimeDisplay;
module.exports = exports['default'];

},{"../../component.js":66,"../../utils/dom.js":144,"../../utils/format-time.js":147}],96:[function(_dereq_,module,exports){
/**
 * @file time-divider.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

/**
 * The separator between the current time and duration.
 * Can be hidden if it's not needed in the design.
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Component
 * @class TimeDivider
 */

var TimeDivider = (function (_Component) {
  _inherits(TimeDivider, _Component);

  function TimeDivider() {
    _classCallCheck(this, TimeDivider);

    _Component.apply(this, arguments);
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  TimeDivider.prototype.createEl = function createEl() {
    return _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-time-control vjs-time-divider',
      innerHTML: '<div><span>/</span></div>'
    });
  };

  return TimeDivider;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('TimeDivider', TimeDivider);
exports['default'] = TimeDivider;
module.exports = exports['default'];

},{"../../component.js":66}],97:[function(_dereq_,module,exports){
/**
 * @file track-button.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _menuMenuButtonJs = _dereq_('../menu/menu-button.js');

var _menuMenuButtonJs2 = _interopRequireDefault(_menuMenuButtonJs);

var _componentJs = _dereq_('../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

/**
 * The base class for buttons that toggle specific text track types (e.g. subtitles)
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends MenuButton
 * @class TrackButton
 */

var TrackButton = (function (_MenuButton) {
  _inherits(TrackButton, _MenuButton);

  function TrackButton(player, options) {
    _classCallCheck(this, TrackButton);

    var tracks = options.tracks;

    _MenuButton.call(this, player, options);

    if (this.items.length <= 1) {
      this.hide();
    }

    if (!tracks) {
      return;
    }

    var updateHandler = Fn.bind(this, this.update);
    tracks.addEventListener('removetrack', updateHandler);
    tracks.addEventListener('addtrack', updateHandler);

    this.player_.on('dispose', function () {
      tracks.removeEventListener('removetrack', updateHandler);
      tracks.removeEventListener('addtrack', updateHandler);
    });
  }

  return TrackButton;
})(_menuMenuButtonJs2['default']);

_componentJs2['default'].registerComponent('TrackButton', TrackButton);
exports['default'] = TrackButton;
module.exports = exports['default'];

},{"../component.js":66,"../menu/menu-button.js":109,"../utils/fn.js":146}],98:[function(_dereq_,module,exports){
/**
 * @file volume-bar.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _sliderSliderJs = _dereq_('../../slider/slider.js');

var _sliderSliderJs2 = _interopRequireDefault(_sliderSliderJs);

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsFnJs = _dereq_('../../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

// Required children

var _volumeLevelJs = _dereq_('./volume-level.js');

var _volumeLevelJs2 = _interopRequireDefault(_volumeLevelJs);

var _volumeDisplayJs = _dereq_('./volume-display.js');

var _volumeDisplayJs2 = _interopRequireDefault(_volumeDisplayJs);

/**
 * The bar that contains the volume level and can be clicked on to adjust the level
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Slider
 * @class VolumeBar
 */

var VolumeBar = (function (_Slider) {
  _inherits(VolumeBar, _Slider);

  function VolumeBar(player, options) {
    _classCallCheck(this, VolumeBar);

    _Slider.call(this, player, options);
    this.on(player, 'volumechange', this.updateARIAAttributes);
    player.ready(Fn.bind(this, this.updateARIAAttributes));
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  VolumeBar.prototype.createEl = function createEl() {
    return _Slider.prototype.createEl.call(this, 'div', {
      className: 'vjs-volume-bar vjs-slider-bar'
    }, {
      'aria-label': 'volume level'
    });
  };

  /**
   * Handle mouse move on volume bar
   *
   * @method handleMouseMove
   */

  VolumeBar.prototype.handleMouseMove = function handleMouseMove(event) {
    this.checkMuted();
    this.player_.volume(this.calculateDistance(event));
  };

  VolumeBar.prototype.checkMuted = function checkMuted() {
    if (this.player_.muted()) {
      this.player_.muted(false);
    }
  };

  /**
   * Get percent of volume level
   *
   * @retun {Number} Volume level percent
   * @method getPercent
   */

  VolumeBar.prototype.getPercent = function getPercent() {
    if (this.player_.muted()) {
      return 0;
    } else {
      return this.player_.volume();
    }
  };

  /**
   * Increase volume level for keyboard users
   *
   * @method stepForward
   */

  VolumeBar.prototype.stepForward = function stepForward() {
    this.checkMuted();
    this.player_.volume(this.player_.volume() + 0.1);
  };

  /**
   * Decrease volume level for keyboard users
   *
   * @method stepBack
   */

  VolumeBar.prototype.stepBack = function stepBack() {
    this.checkMuted();
    this.player_.volume(this.player_.volume() - 0.1);
  };

  /**
   * Update ARIA accessibility attributes
   *
   * @method updateARIAAttributes
   */

  VolumeBar.prototype.updateARIAAttributes = function updateARIAAttributes() {
    // Current value of volume bar as a percentage
    var volume = (this.player_.volume() * 100).toFixed(2);
    this.el_.setAttribute('aria-valuenow', volume);
    this.el_.setAttribute('aria-valuetext', volume + '%');
  };

  return VolumeBar;
})(_sliderSliderJs2['default']);

VolumeBar.prototype.options_ = {
  children: ['volumeLevel', 'volumeDisplay'],
  'barName': 'volumeLevel'
};

VolumeBar.prototype.playerEvent = 'volumechange';

_componentJs2['default'].registerComponent('VolumeBar', VolumeBar);
exports['default'] = VolumeBar;
module.exports = exports['default'];

},{"../../component.js":66,"../../slider/slider.js":119,"../../utils/fn.js":146,"./volume-display.js":100,"./volume-level.js":101}],99:[function(_dereq_,module,exports){
/**
 * @file volume-control.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

// Required children

var _volumeBarJs = _dereq_('./volume-bar.js');

var _volumeBarJs2 = _interopRequireDefault(_volumeBarJs);

/**
 * The component for controlling the volume level
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Component
 * @class VolumeControl
 */

var VolumeControl = (function (_Component) {
  _inherits(VolumeControl, _Component);

  function VolumeControl(player, options) {
    _classCallCheck(this, VolumeControl);

    _Component.call(this, player, options);

    // hide volume controls when they're not supported by the current tech
    if (player.tech_ && player.tech_['featuresVolumeControl'] === false) {
      this.addClass('vjs-hidden');
    }
    this.on(player, 'loadstart', function () {
      if (player.tech_['featuresVolumeControl'] === false) {
        this.addClass('vjs-hidden');
      } else {
        this.removeClass('vjs-hidden');
      }
    });
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  VolumeControl.prototype.createEl = function createEl() {
    return _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-volume-control vjs-control'
    });
  };

  return VolumeControl;
})(_componentJs2['default']);

VolumeControl.prototype.options_ = {
  children: ['volumeBar']
};

_componentJs2['default'].registerComponent('VolumeControl', VolumeControl);
exports['default'] = VolumeControl;
module.exports = exports['default'];

},{"../../component.js":66,"./volume-bar.js":98}],100:[function(_dereq_,module,exports){
/**
 * @file volume-display.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsDomJs = _dereq_('../../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFnJs = _dereq_('../../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _lodashCompatFunctionThrottle = _dereq_('lodash-compat/function/throttle');

var _lodashCompatFunctionThrottle2 = _interopRequireDefault(_lodashCompatFunctionThrottle);

/**
 * The Mouse Time Display component shows the time you will seek to
 * when hovering over the progress bar
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Component
 * @class VolumeDisplay
 */

var VolumeDisplay = (function (_Component) {
  _inherits(VolumeDisplay, _Component);

  function VolumeDisplay(player, options) {
    var _this = this;

    _classCallCheck(this, VolumeDisplay);

    _Component.call(this, player, options);

    this.update(0, 0, 0);

    player.on('ready', function () {
      player.controlBar.progressControl.el().appendChild(_this.tooltip);

      _this.parent = player.controlBar.volumeMenuButton.volumeBar;
      _this.on(_this.parent.el(), 'mousemove', _lodashCompatFunctionThrottle2['default'](Fn.bind(_this, _this.handleMouseMove), 25));
      _this.on(_this.parent.el(), 'mouseenter', _lodashCompatFunctionThrottle2['default'](Fn.bind(_this, _this.handleHover), 25));
      _this.on(_this.parent.el(), 'mouseleave', _lodashCompatFunctionThrottle2['default'](Fn.bind(_this, _this.handleLeave), 25));
    });
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  VolumeDisplay.prototype.createEl = function createEl() {
    this.className = 'vjs-volume-display';
    this.tooltipClassName = this.className + '-tooltip';

    this.tooltip = Dom.createEl('div', {
      className: this.tooltipClassName
    });
    return _Component.prototype.createEl.call(this, 'div', {
      className: this.className
    });
  };

  /**
   * Dispose of the component and all child components
   *
   * @method dispose
   */

  VolumeDisplay.prototype.dispose = function dispose() {
    // Remove tooltipelement from DOM
    if (this.tooltip.parentNode) {
      this.tooltip.parentNode.removeChild(this.tooltip);
    }

    Dom.removeElData(this.tooltip);
    this.tooltip = null;

    _Component.prototype.dispose.call(this);
  };

  VolumeDisplay.prototype.handleMouseMove = function handleMouseMove(event) {
    var newValue = this.calculateDistance(event) * 100;

    var maxLeft = this.parent.width() - this.width();
    var position = event.pageX - Dom.findElPosition(this.el().parentNode).left;
    position = Math.min(Math.max(0, position), maxLeft);

    var tooltipWidth = this.tooltip.offsetWidth;
    var tooltipParentLeft = Dom.findElPosition(this.tooltip.parentNode).left;
    var minTooltipLeft = Dom.findElPosition(this.parent.el()).left - tooltipParentLeft - tooltipWidth / 2;
    var maxTooltipLeft = minTooltipLeft + this.parent.width();

    var tooltipPosition = event.pageX - tooltipParentLeft - tooltipWidth / 2;
    tooltipPosition = Math.min(Math.max(minTooltipLeft, tooltipPosition), maxTooltipLeft);

    this.update(newValue, position, tooltipPosition);
  };

  VolumeDisplay.prototype.handleHover = function handleHover(event) {
    this.player_.trigger('tooltipShown');
    Dom.addElClass(this.tooltip, this.tooltipClassName + '-active');
  };

  VolumeDisplay.prototype.handleLeave = function handleLeave(event) {
    Dom.removeElClass(this.tooltip, this.tooltipClassName + '-active');
  };

  VolumeDisplay.prototype.update = function update(newValue, position, tooltipPosition) {
    var value = Math.round(newValue) + '%';

    this.el().style.left = position + 'px';

    this.tooltip.style.left = tooltipPosition + 'px';
    this.tooltip.innerHTML = value;
  };

  VolumeDisplay.prototype.calculateDistance = function calculateDistance(event) {
    return Dom.getPointerPosition(this.el().parentNode, event).x;
  };

  return VolumeDisplay;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('VolumeDisplay', VolumeDisplay);
exports['default'] = VolumeDisplay;
module.exports = exports['default'];

},{"../../component.js":66,"../../utils/dom.js":144,"../../utils/fn.js":146,"lodash-compat/function/throttle":13}],101:[function(_dereq_,module,exports){
/**
 * @file volume-level.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

/**
 * Shows volume level
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Component
 * @class VolumeLevel
 */

var VolumeLevel = (function (_Component) {
  _inherits(VolumeLevel, _Component);

  function VolumeLevel() {
    _classCallCheck(this, VolumeLevel);

    _Component.apply(this, arguments);
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  VolumeLevel.prototype.createEl = function createEl() {
    return _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-volume-level',
      innerHTML: '<span class="vjs-control-text"></span>'
    });
  };

  return VolumeLevel;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('VolumeLevel', VolumeLevel);
exports['default'] = VolumeLevel;
module.exports = exports['default'];

},{"../../component.js":66}],102:[function(_dereq_,module,exports){
/**
 * @file volume-menu-button.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _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; } }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _componentJs = _dereq_('../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _popupPopupJs = _dereq_('../popup/popup.js');

var _popupPopupJs2 = _interopRequireDefault(_popupPopupJs);

var _popupPopupButtonJs = _dereq_('../popup/popup-button.js');

var _popupPopupButtonJs2 = _interopRequireDefault(_popupPopupButtonJs);

var _muteToggleJs = _dereq_('./mute-toggle.js');

var _muteToggleJs2 = _interopRequireDefault(_muteToggleJs);

var _volumeControlVolumeBarJs = _dereq_('./volume-control/volume-bar.js');

var _volumeControlVolumeBarJs2 = _interopRequireDefault(_volumeControlVolumeBarJs);

/**
 * Button for volume popup
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends PopupButton
 * @class VolumeMenuButton
 */

var VolumeMenuButton = (function (_PopupButton) {
  _inherits(VolumeMenuButton, _PopupButton);

  function VolumeMenuButton(player) {
    var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

    _classCallCheck(this, VolumeMenuButton);

    // Default to inline
    if (options.inline === undefined) {
      options.inline = true;
    }

    // If the vertical option isn't passed at all, default to true.
    if (options.vertical === undefined) {
      // If an inline volumeMenuButton is used, we should default to using
      // a horizontal slider for obvious reasons.
      if (options.inline) {
        options.vertical = false;
      } else {
        options.vertical = true;
      }
    }

    // The vertical option needs to be set on the volumeBar as well,
    // since that will need to be passed along to the VolumeBar constructor
    options.volumeBar = options.volumeBar || {};
    options.volumeBar.vertical = !!options.vertical;

    _PopupButton.call(this, player, options);

    // Same listeners as MuteToggle
    this.on(player, 'volumechange', this.volumeUpdate);
    this.on(player, 'loadstart', this.volumeUpdate);

    // hide mute toggle if the current tech doesn't support volume control
    function updateVisibility() {
      var noMuted = player.tech_ && !player.tech_['featuresMutedControl'];
      var noVolume = player.tech_ && !player.tech_['featuresVolumeControl'];
      this.toggleClass('vjs-hidden', noMuted);
      this.toggleClass('vjs-no-volume', noVolume);
      this.menuContent.toggleClass('vjs-hidden', noVolume);
    }

    updateVisibility.call(this);
    this.on(player, 'loadstart', updateVisibility);

    this.on(this.volumeBar, ['slideractive', 'focus'], function () {
      this.addClass('vjs-slider-active');
    });

    this.on(this.volumeBar, ['sliderinactive', 'blur'], function () {
      this.removeClass('vjs-slider-active');
    });

    this.on(this.volumeBar, ['focus'], function () {
      this.addClass('vjs-lock-showing');
    });

    this.on(this.volumeBar, ['blur'], function () {
      this.removeClass('vjs-lock-showing');
    });
  }

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  VolumeMenuButton.prototype.buildCSSClass = function buildCSSClass() {
    var orientationClass = '';
    if (!!this.options_.vertical) {
      orientationClass = 'vjs-volume-menu-button-vertical';
    } else {
      orientationClass = 'vjs-volume-menu-button-horizontal';
    }

    return 'vjs-volume-menu-button ' + _PopupButton.prototype.buildCSSClass.call(this) + ' ' + orientationClass;
  };

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {Popup} The volume popup button
   * @method createPopup
   */

  VolumeMenuButton.prototype.createPopup = function createPopup() {
    var popup = new _popupPopupJs2['default'](this.player_, {
      contentElType: 'div'
    });

    var vb = new _volumeControlVolumeBarJs2['default'](this.player_, this.options_.volumeBar);

    popup.addChild(vb);

    this.menuContent = popup;
    this.volumeBar = vb;

    this.attachVolumeBarEvents();

    return popup;
  };

  /**
   * Handle click on volume popup and calls super
   *
   * @method handleClick
   */

  VolumeMenuButton.prototype.handleClick = function handleClick() {
    _muteToggleJs2['default'].prototype.handleClick.call(this);
    _PopupButton.prototype.handleClick.call(this);
  };

  VolumeMenuButton.prototype.attachVolumeBarEvents = function attachVolumeBarEvents() {
    this.menuContent.on(['mousedown', 'touchdown'], Fn.bind(this, this.handleMouseDown));
  };

  VolumeMenuButton.prototype.handleMouseDown = function handleMouseDown(event) {
    this.on(['mousemove', 'touchmove'], Fn.bind(this.volumeBar, this.volumeBar.handleMouseMove));
    this.on(this.el_.ownerDocument, ['mouseup', 'touchend'], this.handleMouseUp);
  };

  VolumeMenuButton.prototype.handleMouseUp = function handleMouseUp(event) {
    this.off(['mousemove', 'touchmove'], Fn.bind(this.volumeBar, this.volumeBar.handleMouseMove));
  };

  return VolumeMenuButton;
})(_popupPopupButtonJs2['default']);

VolumeMenuButton.prototype.volumeUpdate = _muteToggleJs2['default'].prototype.update;
VolumeMenuButton.prototype.controlText_ = 'Mute';

_componentJs2['default'].registerComponent('VolumeMenuButton', VolumeMenuButton);
exports['default'] = VolumeMenuButton;
module.exports = exports['default'];

},{"../component.js":66,"../popup/popup-button.js":115,"../popup/popup.js":116,"../utils/fn.js":146,"./mute-toggle.js":72,"./volume-control/volume-bar.js":98}],103:[function(_dereq_,module,exports){
/**
 * @file error-display.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _component = _dereq_('./component');

var _component2 = _interopRequireDefault(_component);

var _modalDialog = _dereq_('./modal-dialog');

var _modalDialog2 = _interopRequireDefault(_modalDialog);

var _utilsDom = _dereq_('./utils/dom');

var Dom = _interopRequireWildcard(_utilsDom);

var _utilsMergeOptions = _dereq_('./utils/merge-options');

var _utilsMergeOptions2 = _interopRequireDefault(_utilsMergeOptions);

/**
 * Display that an error has occurred making the video unplayable.
 *
 * @extends ModalDialog
 * @class ErrorDisplay
 */

var ErrorDisplay = (function (_ModalDialog) {
  _inherits(ErrorDisplay, _ModalDialog);

  /**
   * Constructor for error display modal.
   *
   * @param  {Player} player
   * @param  {Object} [options]
   */

  function ErrorDisplay(player, options) {
    _classCallCheck(this, ErrorDisplay);

    _ModalDialog.call(this, player, options);
    this.on(player, 'error', this.open);
  }

  /**
   * Include the old class for backward-compatibility.
   *
   * This can be removed in 6.0.
   *
   * @method buildCSSClass
   * @deprecated
   * @return {String}
   */

  ErrorDisplay.prototype.buildCSSClass = function buildCSSClass() {
    return 'vjs-error-display ' + _ModalDialog.prototype.buildCSSClass.call(this);
  };

  /**
   * Opens modal dialog only for error codes not configured for ignore.
   */

  ErrorDisplay.prototype.open = function open() {
    var error = this.player().error(),
        ignore = this.options_.ignoreErr;
    if (error && (ignore === true || typeof ignore === 'number' && ignore === error.code || ignore instanceof Array && ignore.indexOf(error.code) >= 0)) {
      this.player().addClass('vjs-error-hidden');
      return this;
    }
    return _ModalDialog.prototype.open.call(this);
  };

  /**
   * Override close of modal dialog for proper player uninit.
   */

  ErrorDisplay.prototype.close = function close() {
    this.player().removeClass('vjs-error-hidden');
    return _ModalDialog.prototype.close.call(this);
  };

  /**
   * Generates the modal content based on the player error.
   *
   * @return {String|Null}
   */

  ErrorDisplay.prototype.content = function content() {
    var error = this.player().error();
    return error ? this.localize(error.message) : '';
  };

  return ErrorDisplay;
})(_modalDialog2['default']);

ErrorDisplay.prototype.options_ = _utilsMergeOptions2['default'](_modalDialog2['default'].prototype.options_, {
  fillAlways: true,
  temporary: false,
  uncloseable: true,
  ignoreErr: false
});

_component2['default'].registerComponent('ErrorDisplay', ErrorDisplay);
exports['default'] = ErrorDisplay;
module.exports = exports['default'];

},{"./component":66,"./modal-dialog":112,"./utils/dom":144,"./utils/merge-options":150}],104:[function(_dereq_,module,exports){
/**
 * @file event-target.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

var _utilsEventsJs = _dereq_('./utils/events.js');

var Events = _interopRequireWildcard(_utilsEventsJs);

var EventTarget = function EventTarget() {};

EventTarget.prototype.allowedEvents_ = {};

EventTarget.prototype.on = function (type, fn) {
  // Remove the addEventListener alias before calling Events.on
  // so we don't get into an infinite type loop
  var ael = this.addEventListener;
  this.addEventListener = function () {};
  Events.on(this, type, fn);
  this.addEventListener = ael;
};
EventTarget.prototype.addEventListener = EventTarget.prototype.on;

EventTarget.prototype.off = function (type, fn) {
  Events.off(this, type, fn);
};
EventTarget.prototype.removeEventListener = EventTarget.prototype.off;

EventTarget.prototype.one = function (type, fn) {
  // Remove the addEventListener alias before calling Events.on
  // so we don't get into an infinite type loop
  var ael = this.addEventListener;
  this.addEventListener = function () {};
  Events.one(this, type, fn);
  this.addEventListener = ael;
};

EventTarget.prototype.trigger = function (event) {
  var type = event.type || event;

  if (typeof event === 'string') {
    event = {
      type: type
    };
  }
  event = Events.fixEvent(event);

  if (this.allowedEvents_[type] && this['on' + type]) {
    this['on' + type](event);
  }

  Events.trigger(this, event);
};
// The standard DOM EventTarget.dispatchEvent() is aliased to trigger()
EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;

exports['default'] = EventTarget;
module.exports = exports['default'];

},{"./utils/events.js":145}],105:[function(_dereq_,module,exports){
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _utilsLog = _dereq_('./utils/log');

var _utilsLog2 = _interopRequireDefault(_utilsLog);

/*
 * @file extend.js
 *
 * A combination of node inherits and babel's inherits (after transpile).
 * Both work the same but node adds `super_` to the subClass
 * and Bable adds the superClass as __proto__. Both seem useful.
 */
var _inherits = function _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) {
    // node
    subClass.super_ = superClass;
  }
};

/*
 * Function for subclassing using the same inheritance that
 * videojs uses internally
 * ```js
 * var Button = videojs.getComponent('Button');
 * ```
 * ```js
 * var MyButton = videojs.extend(Button, {
 *   constructor: function(player, options) {
 *     Button.call(this, player, options);
 *   },
 *   onClick: function() {
 *     // doSomething
 *   }
 * });
 * ```
 */
var extendFn = function extendFn(superClass) {
  var subClassMethods = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

  var subClass = function subClass() {
    superClass.apply(this, arguments);
  };
  var methods = {};

  if (typeof subClassMethods === 'object') {
    if (typeof subClassMethods.init === 'function') {
      _utilsLog2['default'].warn('Constructor logic via init() is deprecated; please use constructor() instead.');
      subClassMethods.constructor = subClassMethods.init;
    }
    if (subClassMethods.constructor !== Object.prototype.constructor) {
      subClass = subClassMethods.constructor;
    }
    methods = subClassMethods;
  } else if (typeof subClassMethods === 'function') {
    subClass = subClassMethods;
  }

  _inherits(subClass, superClass);

  // Extend subObj's prototype with functions and other properties from props
  for (var name in methods) {
    if (methods.hasOwnProperty(name)) {
      subClass.prototype[name] = methods[name];
    }
  }

  return subClass;
};

exports['default'] = extendFn;
module.exports = exports['default'];

},{"./utils/log":149}],106:[function(_dereq_,module,exports){
/**
 * @file fullscreen-api.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

/*
 * Store the browser-specific methods for the fullscreen API
 * @type {Object|undefined}
 * @private
 */
var FullscreenApi = {};

// browser API methods
// map approach from Screenful.js - https://github.com/sindresorhus/screenfull.js
var apiMap = [
// Spec: https://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html
['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror'],
// WebKit
['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror'],
// Old WebKit (Safari 5.1)
['webkitRequestFullScreen', 'webkitCancelFullScreen', 'webkitCurrentFullScreenElement', 'webkitCancelFullScreen', 'webkitfullscreenchange', 'webkitfullscreenerror'],
// Mozilla
['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror'],
// Microsoft
['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError']];

var specApi = apiMap[0];
var browserApi = undefined;

// determine the supported set of functions
for (var i = 0; i < apiMap.length; i++) {
  // check for exitFullscreen function
  if (apiMap[i][1] in _globalDocument2['default']) {
    browserApi = apiMap[i];
    break;
  }
}

// map the browser API names to the spec API names
if (browserApi) {
  for (var i = 0; i < browserApi.length; i++) {
    FullscreenApi[specApi[i]] = browserApi[i];
  }
}

exports['default'] = FullscreenApi;
module.exports = exports['default'];

},{"global/document":7}],107:[function(_dereq_,module,exports){
/**
 * @file loading-spinner.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _component = _dereq_('./component');

var _component2 = _interopRequireDefault(_component);

/* Loading Spinner
================================================================================ */
/**
 * Loading spinner for waiting events
 *
 * @extends Component
 * @class LoadingSpinner
 */

var LoadingSpinner = (function (_Component) {
  _inherits(LoadingSpinner, _Component);

  function LoadingSpinner() {
    _classCallCheck(this, LoadingSpinner);

    _Component.apply(this, arguments);
  }

  /**
   * Create the component's DOM element
   *
   * @method createEl
   */

  LoadingSpinner.prototype.createEl = function createEl() {
    return _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-loading-spinner',
      dir: 'ltr'
    });
  };

  return LoadingSpinner;
})(_component2['default']);

_component2['default'].registerComponent('LoadingSpinner', LoadingSpinner);
exports['default'] = LoadingSpinner;
module.exports = exports['default'];

},{"./component":66}],108:[function(_dereq_,module,exports){
/**
 * @file media-error.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _objectAssign = _dereq_('object.assign');

var _objectAssign2 = _interopRequireDefault(_objectAssign);

/*
 * Custom MediaError to mimic the HTML5 MediaError
 *
 * @param {Number} code The media error code
 */
var MediaError = function MediaError(code) {
  if (typeof code === 'number') {
    this.code = code;
  } else if (typeof code === 'string') {
    // default code is zero, so this is a custom error
    this.message = code;
  } else if (typeof code === 'object') {
    // object
    _objectAssign2['default'](this, code);
  }

  if (!this.message) {
    this.message = MediaError.defaultMessages[this.code] || '';
  }
};

/*
 * The error code that refers two one of the defined
 * MediaError types
 *
 * @type {Number}
 */
MediaError.prototype.code = 0;

/*
 * An optional message to be shown with the error.
 * Message is not part of the HTML5 video spec
 * but allows for more informative custom errors.
 *
 * @type {String}
 */
MediaError.prototype.message = '';

/*
 * An optional status code that can be set by plugins
 * to allow even more detail about the error.
 * For example the HLS plugin might provide the specific
 * HTTP status code that was returned when the error
 * occurred, then allowing a custom error overlay
 * to display more information.
 *
 * @type {Array}
 */
MediaError.prototype.status = null;

MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', // = 0
'MEDIA_ERR_ABORTED', // = 1
'MEDIA_ERR_NETWORK', // = 2
'MEDIA_ERR_DECODE', // = 3
'MEDIA_ERR_SRC_NOT_SUPPORTED', // = 4
'MEDIA_ERR_ENCRYPTED' // = 5
];

MediaError.defaultMessages = {
  1: 'You aborted the media playback',
  2: 'A network error caused the media download to fail part-way.',
  3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.',
  4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.',
  5: 'The media is encrypted and we do not have the keys to decrypt it.'
};

// Add types as properties on MediaError
// e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
  MediaError[MediaError.errorTypes[errNum]] = errNum;
  // values should be accessible on both the class and instance
  MediaError.prototype[MediaError.errorTypes[errNum]] = errNum;
}

exports['default'] = MediaError;
module.exports = exports['default'];

},{"object.assign":53}],109:[function(_dereq_,module,exports){
/**
 * @file menu-button.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _clickableComponentJs = _dereq_('../clickable-component.js');

var _clickableComponentJs2 = _interopRequireDefault(_clickableComponentJs);

var _componentJs = _dereq_('../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _menuJs = _dereq_('./menu.js');

var _menuJs2 = _interopRequireDefault(_menuJs);

var _utilsDomJs = _dereq_('../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsToTitleCaseJs = _dereq_('../utils/to-title-case.js');

var _utilsToTitleCaseJs2 = _interopRequireDefault(_utilsToTitleCaseJs);

/**
 * A button class with a popup menu
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Button
 * @class MenuButton
 */

var MenuButton = (function (_ClickableComponent) {
  _inherits(MenuButton, _ClickableComponent);

  function MenuButton(player) {
    var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

    _classCallCheck(this, MenuButton);

    _ClickableComponent.call(this, player, options);

    this.update();

    this.enabled_ = true;

    this.el_.setAttribute('aria-haspopup', 'true');
    this.el_.setAttribute('role', 'menuitem');
    this.on('keydown', this.handleSubmenuKeyPress);
  }

  /**
   * Update menu
   *
   * @method update
   */

  MenuButton.prototype.update = function update() {
    var menu = this.createMenu();

    if (this.menu) {
      this.removeChild(this.menu);
    }

    this.menu = menu;
    this.addChild(menu);

    /**
     * Track the state of the menu button
     *
     * @type {Boolean}
     * @private
     */
    this.buttonPressed_ = false;
    this.el_.setAttribute('aria-expanded', 'false');

    if (this.items && this.items.length === 0) {
      this.hide();
    } else if (this.items && this.items.length > 1) {
      this.show();
    }
  };

  /**
   * Create menu
   *
   * @return {Menu} The constructed menu
   * @method createMenu
   */

  MenuButton.prototype.createMenu = function createMenu() {
    var menu = new _menuJs2['default'](this.player_);

    // Add a title list item to the top
    if (this.options_.title) {
      var title = Dom.createEl('li', {
        className: 'vjs-menu-title',
        innerHTML: _utilsToTitleCaseJs2['default'](this.options_.title),
        tabIndex: -1
      });
      menu.children_.unshift(title);
      Dom.insertElFirst(title, menu.contentEl());
    }

    this.items = this['createItems']();

    if (this.items) {
      // Add menu items to the menu
      for (var i = 0; i < this.items.length; i++) {
        menu.addItem(this.items[i]);
      }
    }

    return menu;
  };

  /**
   * Create the list of menu items. Specific to each subclass.
   *
   * @method createItems
   */

  MenuButton.prototype.createItems = function createItems() {};

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  MenuButton.prototype.createEl = function createEl() {
    return _ClickableComponent.prototype.createEl.call(this, 'div', {
      className: this.buildCSSClass()
    });
  };

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  MenuButton.prototype.buildCSSClass = function buildCSSClass() {
    var menuButtonClass = 'vjs-menu-button';

    // If the inline option is passed, we want to use different styles altogether.
    if (this.options_.inline === true) {
      menuButtonClass += '-inline';
    } else {
      menuButtonClass += '-popup';
    }

    return 'vjs-menu-button ' + menuButtonClass + ' ' + _ClickableComponent.prototype.buildCSSClass.call(this);
  };

  /**
   * When you click the button it adds focus, which
   * will show the menu indefinitely.
   * So we'll remove focus when the mouse leaves the button.
   * Focus is needed for tab navigation.
   * Allow sub components to stack CSS class names
   *
   * @method handleClick
   */

  MenuButton.prototype.handleClick = function handleClick() {
    this.one('mouseout', Fn.bind(this, function () {
      this.menu.unlockShowing();
      this.el_.blur();
    }));
    if (this.buttonPressed_) {
      this.unpressButton();
    } else {
      this.pressButton();
    }
  };

  /**
   * Handle key press on menu
   *
   * @param {Object} event Key press event
   * @method handleKeyPress
   */

  MenuButton.prototype.handleKeyPress = function handleKeyPress(event) {

    // Escape (27) key or Tab (9) key unpress the 'button'
    if (event.which === 27 || event.which === 9) {
      if (this.buttonPressed_) {
        this.unpressButton();
      }
      // Don't preventDefault for Tab key - we still want to lose focus
      if (event.which !== 9) {
        event.preventDefault();
      }
      // Up (38) key or Down (40) key press the 'button'
    } else if (event.which === 38 || event.which === 40) {
        if (!this.buttonPressed_) {
          this.pressButton();
          event.preventDefault();
        }
      } else {
        _ClickableComponent.prototype.handleKeyPress.call(this, event);
      }
  };

  /**
   * Handle key press on submenu
   *
   * @param {Object} event Key press event
   * @method handleSubmenuKeyPress
   */

  MenuButton.prototype.handleSubmenuKeyPress = function handleSubmenuKeyPress(event) {

    // Escape (27) key or Tab (9) key unpress the 'button'
    if (event.which === 27 || event.which === 9) {
      if (this.buttonPressed_) {
        this.unpressButton();
      }
      // Don't preventDefault for Tab key - we still want to lose focus
      if (event.which !== 9) {
        event.preventDefault();
      }
    }
  };

  /**
   * Makes changes based on button pressed
   *
   * @method pressButton
   */

  MenuButton.prototype.pressButton = function pressButton() {
    if (this.enabled_) {
      this.buttonPressed_ = true;
      this.menu.lockShowing();
      this.el_.setAttribute('aria-expanded', 'true');
      this.menu.focus(); // set the focus into the submenu
    }
  };

  /**
   * Makes changes based on button unpressed
   *
   * @method unpressButton
   */

  MenuButton.prototype.unpressButton = function unpressButton() {
    if (this.enabled_) {
      this.buttonPressed_ = false;
      this.menu.unlockShowing();
      this.el_.setAttribute('aria-expanded', 'false');
      this.el_.focus(); // Set focus back to this menu button
    }
  };

  /**
   * Disable the menu button
   *
   * @return {Component}
   * @method disable
   */

  MenuButton.prototype.disable = function disable() {
    // Unpress, but don't force focus on this button
    this.buttonPressed_ = false;
    this.menu.unlockShowing();
    this.el_.setAttribute('aria-expanded', 'false');

    this.enabled_ = false;

    return _ClickableComponent.prototype.disable.call(this);
  };

  /**
   * Enable the menu button
   *
   * @return {Component}
   * @method disable
   */

  MenuButton.prototype.enable = function enable() {
    this.enabled_ = true;

    return _ClickableComponent.prototype.enable.call(this);
  };

  /**
   * Return handler for the tooltip
   *
   * @return {Object} Dom element to serve as a handler for the tooltip
   * @method tooltipHandler
   */

  MenuButton.prototype.tooltipHandler = function tooltipHandler() {
    // TODO: Fix menu button to have a real icon element to make it as tooltip handler
    return;
  };

  return MenuButton;
})(_clickableComponentJs2['default']);

_componentJs2['default'].registerComponent('MenuButton', MenuButton);
exports['default'] = MenuButton;
module.exports = exports['default'];

},{"../clickable-component.js":64,"../component.js":66,"../utils/dom.js":144,"../utils/fn.js":146,"../utils/to-title-case.js":153,"./menu.js":111}],110:[function(_dereq_,module,exports){
/**
 * @file menu-item.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _clickableComponentJs = _dereq_('../clickable-component.js');

var _clickableComponentJs2 = _interopRequireDefault(_clickableComponentJs);

var _componentJs = _dereq_('../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _objectAssign = _dereq_('object.assign');

var _objectAssign2 = _interopRequireDefault(_objectAssign);

/**
 * The component for a menu item. `<li>`
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Button
 * @class MenuItem
 */

var MenuItem = (function (_ClickableComponent) {
  _inherits(MenuItem, _ClickableComponent);

  function MenuItem(player, options) {
    _classCallCheck(this, MenuItem);

    _ClickableComponent.call(this, player, options);

    this.selectable = options['selectable'];

    this.selected(options['selected']);

    if (this.selectable) {
      // TODO: May need to be either menuitemcheckbox or menuitemradio,
      //       and may need logical grouping of menu items.
      this.el_.setAttribute('role', 'menuitemcheckbox');
    } else {
      this.el_.setAttribute('role', 'menuitem');
    }
  }

  /**
   * Create the component's DOM element
   *
   * @param {String=} type Desc
   * @param {Object=} props Desc
   * @return {Element}
   * @method createEl
   */

  MenuItem.prototype.createEl = function createEl(type, props, attrs) {
    return _ClickableComponent.prototype.createEl.call(this, 'li', _objectAssign2['default']({
      className: 'vjs-menu-item',
      innerHTML: this.localize(this.options_['label']),
      tabIndex: -1
    }, props), attrs);
  };

  /**
   * Handle a click on the menu item, and set it to selected
   *
   * @method handleClick
   */

  MenuItem.prototype.handleClick = function handleClick() {
    this.selected(true);
  };

  /**
   * Set this menu item as selected or not
   *
   * @param  {Boolean} selected
   * @method selected
   */

  MenuItem.prototype.selected = function selected(_selected) {
    if (this.selectable) {
      if (_selected) {
        this.addClass('vjs-selected');
        this.el_.setAttribute('aria-checked', 'true');
        // aria-checked isn't fully supported by browsers/screen readers,
        // so indicate selected state to screen reader in the control text.
        this.controlText(', selected');
      } else {
        this.removeClass('vjs-selected');
        this.el_.setAttribute('aria-checked', 'false');
        // Indicate un-selected state to screen reader
        // Note that a space clears out the selected state text
        this.controlText(' ');
      }
    }
  };

  /**
   * Return handler for the tooltip
   *
   * @return {Object} Dom element to serve as a handler for the tooltip
   * @method tooltipHandler
   */

  MenuItem.prototype.tooltipHandler = function tooltipHandler() {
    if (this.options_.tooltip !== undefined) {
      return _ClickableComponent.prototype.tooltipHandler.call(this);
    }
  };

  return MenuItem;
})(_clickableComponentJs2['default']);

_componentJs2['default'].registerComponent('MenuItem', MenuItem);
exports['default'] = MenuItem;
module.exports = exports['default'];

},{"../clickable-component.js":64,"../component.js":66,"object.assign":53}],111:[function(_dereq_,module,exports){
/**
 * @file menu.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsDomJs = _dereq_('../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsEventsJs = _dereq_('../utils/events.js');

var Events = _interopRequireWildcard(_utilsEventsJs);

/**
 * The Menu component is used to build pop up menus, including subtitle and
 * captions selection menus.
 *
 * @extends Component
 * @class Menu
 */

var Menu = (function (_Component) {
  _inherits(Menu, _Component);

  function Menu(player, options) {
    _classCallCheck(this, Menu);

    _Component.call(this, player, options);

    this.focusedChild_ = -1;

    this.on('keydown', this.handleKeyPress);
  }

  /**
   * Add a menu item to the menu
   *
   * @param {Object|String} component Component or component type to add
   * @method addItem
   */

  Menu.prototype.addItem = function addItem(component) {
    this.addChild(component);
    component.on('click', Fn.bind(this, function () {
      this.unlockShowing();
      //TODO: Need to set keyboard focus back to the menuButton
    }));
  };

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  Menu.prototype.createEl = function createEl() {
    var contentElType = this.options_.contentElType || 'ul';
    this.contentEl_ = Dom.createEl(contentElType, {
      className: 'vjs-menu-content'
    });
    this.contentEl_.setAttribute('role', 'menu');
    var el = _Component.prototype.createEl.call(this, 'div', {
      append: this.contentEl_,
      className: 'vjs-menu'
    });
    el.setAttribute('role', 'presentation');
    el.appendChild(this.contentEl_);

    // Prevent clicks from bubbling up. Needed for Menu Buttons,
    // where a click on the parent is significant
    Events.on(el, 'click', function (event) {
      event.preventDefault();
      event.stopImmediatePropagation();
    });

    return el;
  };

  /**
   * Handle key press for menu
   *
   * @param {Object} event Event object
   * @method handleKeyPress
   */

  Menu.prototype.handleKeyPress = function handleKeyPress(event) {
    if (event.which === 37 || event.which === 40) {
      // Left and Down Arrows
      event.preventDefault();
      this.stepForward();
    } else if (event.which === 38 || event.which === 39) {
      // Up and Right Arrows
      event.preventDefault();
      this.stepBack();
    }
  };

  /**
   * Move to next (lower) menu item for keyboard users
   *
   * @method stepForward
   */

  Menu.prototype.stepForward = function stepForward() {
    var stepChild = 0;

    if (this.focusedChild_ !== undefined) {
      stepChild = this.focusedChild_ + 1;
    }
    this.focus(stepChild);
  };

  /**
   * Move to previous (higher) menu item for keyboard users
   *
   * @method stepBack
   */

  Menu.prototype.stepBack = function stepBack() {
    var stepChild = 0;

    if (this.focusedChild_ !== undefined) {
      stepChild = this.focusedChild_ - 1;
    }
    this.focus(stepChild);
  };

  /**
   * Set focus on a menu item in the menu
   *
   * @param {Object|String} item Index of child item set focus on
   * @method focus
   */

  Menu.prototype.focus = function focus() {
    var item = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0];

    var children = this.children().slice();
    var haveTitle = children.length && children[0].className && /vjs-menu-title/.test(children[0].className);

    if (haveTitle) {
      children.shift();
    }

    if (children.length > 0) {
      if (item < 0) {
        item = 0;
      } else if (item >= children.length) {
        item = children.length - 1;
      }

      this.focusedChild_ = item;

      children[item].el_.focus();
    }
  };

  return Menu;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('Menu', Menu);
exports['default'] = Menu;
module.exports = exports['default'];

},{"../component.js":66,"../utils/dom.js":144,"../utils/events.js":145,"../utils/fn.js":146}],112:[function(_dereq_,module,exports){
/**
 * @file modal-dialog.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _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; } }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _utilsDom = _dereq_('./utils/dom');

var Dom = _interopRequireWildcard(_utilsDom);

var _utilsFn = _dereq_('./utils/fn');

var Fn = _interopRequireWildcard(_utilsFn);

var _utilsLog = _dereq_('./utils/log');

var _utilsLog2 = _interopRequireDefault(_utilsLog);

var _component = _dereq_('./component');

var _component2 = _interopRequireDefault(_component);

var _closeButton = _dereq_('./close-button');

var _closeButton2 = _interopRequireDefault(_closeButton);

var MODAL_CLASS_NAME = 'vjs-modal-dialog';
var ESC = 27;

/**
 * The `ModalDialog` displays over the video and its controls, which blocks
 * interaction with the player until it is closed.
 *
 * Modal dialogs include a "Close" button and will close when that button
 * is activated - or when ESC is pressed anywhere.
 *
 * @extends Component
 * @class ModalDialog
 */

var ModalDialog = (function (_Component) {
  _inherits(ModalDialog, _Component);

  /**
   * Constructor for modals.
   *
   * @param  {Player} player
   * @param  {Object} [options]
   * @param  {Mixed} [options.content=undefined]
   *         Provide customized content for this modal.
   *
   * @param  {String} [options.description]
   *         A text description for the modal, primarily for accessibility.
   *
   * @param  {Boolean} [options.fillAlways=false]
   *         Normally, modals are automatically filled only the first time
   *         they open. This tells the modal to refresh its content
   *         every time it opens.
   *
   * @param  {String} [options.label]
   *         A text label for the modal, primarily for accessibility.
   *
   * @param  {Boolean} [options.temporary=true]
   *         If `true`, the modal can only be opened once; it will be
   *         disposed as soon as it's closed.
   *
   * @param  {Boolean} [options.uncloseable=false]
   *         If `true`, the user will not be able to close the modal
   *         through the UI in the normal ways. Programmatic closing is
   *         still possible.
   *
   */

  function ModalDialog(player, options) {
    _classCallCheck(this, ModalDialog);

    _Component.call(this, player, options);
    this.opened_ = this.hasBeenOpened_ = this.hasBeenFilled_ = false;

    this.closeable(!this.options_.uncloseable);
    this.content(this.options_.content);

    // Make sure the contentEl is defined AFTER any children are initialized
    // because we only want the contents of the modal in the contentEl
    // (not the UI elements like the close button).
    this.contentEl_ = Dom.createEl('div', {
      className: MODAL_CLASS_NAME + '-content'
    }, {
      role: 'document'
    });

    this.descEl_ = Dom.createEl('p', {
      className: MODAL_CLASS_NAME + '-description vjs-offscreen',
      id: this.el().getAttribute('aria-describedby')
    });

    Dom.textContent(this.descEl_, this.description());
    this.el_.appendChild(this.descEl_);
    this.el_.appendChild(this.contentEl_);
  }

  /*
   * Modal dialog default options.
   *
   * @type {Object}
   * @private
   */

  /**
   * Create the modal's DOM element
   *
   * @method createEl
   * @return {Element}
   */

  ModalDialog.prototype.createEl = function createEl() {
    return _Component.prototype.createEl.call(this, 'div', {
      className: this.buildCSSClass(),
      tabIndex: -1
    }, {
      'aria-describedby': this.id() + '_description',
      'aria-hidden': 'true',
      'aria-label': this.label(),
      role: 'dialog'
    });
  };

  /**
   * Build the modal's CSS class.
   *
   * @method buildCSSClass
   * @return {String}
   */

  ModalDialog.prototype.buildCSSClass = function buildCSSClass() {
    return MODAL_CLASS_NAME + ' vjs-hidden ' + _Component.prototype.buildCSSClass.call(this);
  };

  /**
   * Handles key presses on the document, looking for ESC, which closes
   * the modal.
   *
   * @method handleKeyPress
   * @param  {Event} e
   */

  ModalDialog.prototype.handleKeyPress = function handleKeyPress(e) {
    if (e.which === ESC && this.closeable()) {
      this.close();
    }
  };

  /**
   * Returns the label string for this modal. Primarily used for accessibility.
   *
   * @return {String}
   */

  ModalDialog.prototype.label = function label() {
    return this.options_.label || this.localize('Modal Window');
  };

  /**
   * Returns the description string for this modal. Primarily used for
   * accessibility.
   *
   * @return {String}
   */

  ModalDialog.prototype.description = function description() {
    var desc = this.options_.description || this.localize('This is a modal window.');

    // Append a universal closeability message if the modal is closeable.
    if (this.closeable()) {
      desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.');
    }

    return desc;
  };

  /**
   * Opens the modal.
   *
   * @method open
   * @return {ModalDialog}
   */

  ModalDialog.prototype.open = function open() {
    if (!this.opened_) {
      var player = this.player();

      this.trigger('beforemodalopen');
      this.opened_ = true;

      // Fill content if the modal has never opened before and
      // never been filled.
      if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
        this.fill();
      }

      // If the player was playing, pause it and take note of its previously
      // playing state.
      this.wasPlaying_ = !player.paused();

      if (this.wasPlaying_) {
        player.pause();
      }

      if (this.closeable()) {
        this.on(this.el_.ownerDocument, 'keydown', Fn.bind(this, this.handleKeyPress));
      }

      player.controls(false);
      this.show();
      this.el().setAttribute('aria-hidden', 'false');
      this.trigger('modalopen');
      this.hasBeenOpened_ = true;
    }
    return this;
  };

  /**
   * Whether or not the modal is opened currently.
   *
   * @method opened
   * @param  {Boolean} [value]
   *         If given, it will open (`true`) or close (`false`) the modal.
   *
   * @return {Boolean}
   */

  ModalDialog.prototype.opened = function opened(value) {
    if (typeof value === 'boolean') {
      this[value ? 'open' : 'close']();
    }
    return this.opened_;
  };

  /**
   * Closes the modal.
   *
   * @method close
   * @return {ModalDialog}
   */

  ModalDialog.prototype.close = function close() {
    if (this.opened_) {
      var player = this.player();

      this.trigger('beforemodalclose');
      this.opened_ = false;

      if (this.wasPlaying_) {
        player.play();
      }

      if (this.closeable()) {
        this.off(this.el_.ownerDocument, 'keydown', Fn.bind(this, this.handleKeyPress));
      }

      player.controls(true);
      this.hide();
      this.el().setAttribute('aria-hidden', 'true');
      this.trigger('modalclose');

      if (this.options_.temporary) {
        this.dispose();
      }
    }
    return this;
  };

  /**
   * Whether or not the modal is closeable via the UI.
   *
   * @method closeable
   * @param  {Boolean} [value]
   *         If given as a Boolean, it will set the `closeable` option.
   *
   * @return {Boolean}
   */

  ModalDialog.prototype.closeable = function closeable(value) {
    if (typeof value === 'boolean') {
      var closeable = this.closeable_ = !!value;
      var _close = this.getChild('closeButton');

      // If this is being made closeable and has no close button, add one.
      if (closeable && !_close) {

        // The close button should be a child of the modal - not its
        // content element, so temporarily change the content element.
        var temp = this.contentEl_;
        this.contentEl_ = this.el_;
        _close = this.addChild('closeButton');
        this.contentEl_ = temp;
        this.on(_close, 'close', this.close);
      }

      // If this is being made uncloseable and has a close button, remove it.
      if (!closeable && _close) {
        this.off(_close, 'close', this.close);
        this.removeChild(_close);
        _close.dispose();
      }
    }
    return this.closeable_;
  };

  /**
   * Fill the modal's content element with the modal's "content" option.
   *
   * The content element will be emptied before this change takes place.
   *
   * @method fill
   * @return {ModalDialog}
   */

  ModalDialog.prototype.fill = function fill() {
    return this.fillWith(this.content());
  };

  /**
   * Fill the modal's content element with arbitrary content.
   *
   * The content element will be emptied before this change takes place.
   *
   * @method fillWith
   * @param  {Mixed} [content]
   *         The same rules apply to this as apply to the `content` option.
   *
   * @return {ModalDialog}
   */

  ModalDialog.prototype.fillWith = function fillWith(content) {
    var contentEl = this.contentEl();
    var parentEl = contentEl.parentNode;
    var nextSiblingEl = contentEl.nextSibling;

    this.trigger('beforemodalfill');
    this.hasBeenFilled_ = true;

    // Detach the content element from the DOM before performing
    // manipulation to avoid modifying the live DOM multiple times.
    parentEl.removeChild(contentEl);
    this.empty();
    Dom.insertContent(contentEl, content);
    this.trigger('modalfill');

    // Re-inject the re-filled content element.
    if (nextSiblingEl) {
      parentEl.insertBefore(contentEl, nextSiblingEl);
    } else {
      parentEl.appendChild(contentEl);
    }

    return this;
  };

  /**
   * Empties the content element.
   *
   * This happens automatically anytime the modal is filled.
   *
   * @method empty
   * @return {ModalDialog}
   */

  ModalDialog.prototype.empty = function empty() {
    this.trigger('beforemodalempty');
    Dom.emptyEl(this.contentEl());
    this.trigger('modalempty');
    return this;
  };

  /**
   * Gets or sets the modal content, which gets normalized before being
   * rendered into the DOM.
   *
   * This does not update the DOM or fill the modal, but it is called during
   * that process.
   *
   * @method content
   * @param  {Mixed} [value]
   *         If defined, sets the internal content value to be used on the
   *         next call(s) to `fill`. This value is normalized before being
   *         inserted. To "clear" the internal content value, pass `null`.
   *
   * @return {Mixed}
   */

  ModalDialog.prototype.content = function content(value) {
    if (typeof value !== 'undefined') {
      this.content_ = value;
    }
    return this.content_;
  };

  return ModalDialog;
})(_component2['default']);

ModalDialog.prototype.options_ = {
  temporary: true
};

_component2['default'].registerComponent('ModalDialog', ModalDialog);
exports['default'] = ModalDialog;
module.exports = exports['default'];

},{"./close-button":65,"./component":66,"./utils/dom":144,"./utils/fn":146,"./utils/log":149}],113:[function(_dereq_,module,exports){
/**
 * @file player.js
 */
// Subclasses Component
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('./component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _utilsEventsJs = _dereq_('./utils/events.js');

var Events = _interopRequireWildcard(_utilsEventsJs);

var _utilsDomJs = _dereq_('./utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFnJs = _dereq_('./utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsGuidJs = _dereq_('./utils/guid.js');

var Guid = _interopRequireWildcard(_utilsGuidJs);

var _utilsBrowserJs = _dereq_('./utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

var _utilsLogJs = _dereq_('./utils/log.js');

var _utilsLogJs2 = _interopRequireDefault(_utilsLogJs);

var _utilsToTitleCaseJs = _dereq_('./utils/to-title-case.js');

var _utilsToTitleCaseJs2 = _interopRequireDefault(_utilsToTitleCaseJs);

var _utilsTimeRangesJs = _dereq_('./utils/time-ranges.js');

var _utilsBufferJs = _dereq_('./utils/buffer.js');

var _utilsStylesheetJs = _dereq_('./utils/stylesheet.js');

var stylesheet = _interopRequireWildcard(_utilsStylesheetJs);

var _fullscreenApiJs = _dereq_('./fullscreen-api.js');

var _fullscreenApiJs2 = _interopRequireDefault(_fullscreenApiJs);

var _mediaErrorJs = _dereq_('./media-error.js');

var _mediaErrorJs2 = _interopRequireDefault(_mediaErrorJs);

var _safeJsonParseTuple = _dereq_('safe-json-parse/tuple');

var _safeJsonParseTuple2 = _interopRequireDefault(_safeJsonParseTuple);

var _objectAssign = _dereq_('object.assign');

var _objectAssign2 = _interopRequireDefault(_objectAssign);

var _utilsMergeOptionsJs = _dereq_('./utils/merge-options.js');

var _utilsMergeOptionsJs2 = _interopRequireDefault(_utilsMergeOptionsJs);

var _tracksTextTrackListConverterJs = _dereq_('./tracks/text-track-list-converter.js');

var _tracksTextTrackListConverterJs2 = _interopRequireDefault(_tracksTextTrackListConverterJs);

var _tracksAudioTrackListJs = _dereq_('./tracks/audio-track-list.js');

var _tracksAudioTrackListJs2 = _interopRequireDefault(_tracksAudioTrackListJs);

var _tracksVideoTrackListJs = _dereq_('./tracks/video-track-list.js');

var _tracksVideoTrackListJs2 = _interopRequireDefault(_tracksVideoTrackListJs);

// Include required child components (importing also registers them)

var _techLoaderJs = _dereq_('./tech/loader.js');

var _techLoaderJs2 = _interopRequireDefault(_techLoaderJs);

var _posterImageJs = _dereq_('./poster-image.js');

var _posterImageJs2 = _interopRequireDefault(_posterImageJs);

var _tracksTextTrackDisplayJs = _dereq_('./tracks/text-track-display.js');

var _tracksTextTrackDisplayJs2 = _interopRequireDefault(_tracksTextTrackDisplayJs);

var _loadingSpinnerJs = _dereq_('./loading-spinner.js');

var _loadingSpinnerJs2 = _interopRequireDefault(_loadingSpinnerJs);

var _bigPlayButtonJs = _dereq_('./big-play-button.js');

var _bigPlayButtonJs2 = _interopRequireDefault(_bigPlayButtonJs);

var _controlBarControlBarJs = _dereq_('./control-bar/control-bar.js');

var _controlBarControlBarJs2 = _interopRequireDefault(_controlBarControlBarJs);

var _errorDisplayJs = _dereq_('./error-display.js');

var _errorDisplayJs2 = _interopRequireDefault(_errorDisplayJs);

var _tracksTextTrackSettingsJs = _dereq_('./tracks/text-track-settings.js');

var _tracksTextTrackSettingsJs2 = _interopRequireDefault(_tracksTextTrackSettingsJs);

var _modalDialog = _dereq_('./modal-dialog');

var _modalDialog2 = _interopRequireDefault(_modalDialog);

// Require html5 tech, at least for disposing the original video tag

var _techTechJs = _dereq_('./tech/tech.js');

var _techTechJs2 = _interopRequireDefault(_techTechJs);

var _techHtml5Js = _dereq_('./tech/html5.js');

var _techHtml5Js2 = _interopRequireDefault(_techHtml5Js);

/**
 * An instance of the `Player` class is created when any of the Video.js setup methods are used to initialize a video.
 * ```js
 * var myPlayer = videojs('example_video_1');
 * ```
 * In the following example, the `data-setup` attribute tells the Video.js library to create a player instance when the library is ready.
 * ```html
 * <video id="example_video_1" data-setup='{}' controls>
 *   <source src="my-source.mp4" type="video/mp4">
 * </video>
 * ```
 * After an instance has been created it can be accessed globally using `Video('example_video_1')`.
 *
 * @param {Element} tag        The original video tag used for configuring options
 * @param {Object=} options    Object of option names and values
 * @param {Function=} ready    Ready callback function
 * @extends Component
 * @class Player
 */

var Player = (function (_Component) {
  _inherits(Player, _Component);

  /**
   * player's constructor function
   *
   * @constructs
   * @method init
   * @param {Element} tag        The original video tag used for configuring options
   * @param {Object=} options    Player options
   * @param {Function=} ready    Ready callback function
   */

  function Player(tag, options, ready) {
    var _this = this;

    _classCallCheck(this, Player);

    // Make sure tag ID exists
    tag.id = tag.id || 'vjs_video_' + Guid.newGUID();

    // Set Options
    // The options argument overrides options set in the video tag
    // which overrides globally set options.
    // This latter part coincides with the load order
    // (tag must exist before Player)
    options = _objectAssign2['default'](Player.getTagSettings(tag), options);

    // Delay the initialization of children because we need to set up
    // player properties first, and can't use `this` before `super()`
    options.initChildren = false;

    // Same with creating the element
    options.createEl = false;

    // we don't want the player to report touch activity on itself
    // see enableTouchActivity in Component
    options.reportTouchActivity = false;

    // Run base component initializing with new options
    _Component.call(this, null, options, ready);

    // if the global option object was accidentally blown away by
    // someone, bail early with an informative error
    if (!this.options_ || !this.options_.techOrder || !this.options_.techOrder.length) {
      throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
    }

    this.tag = tag; // Store the original tag used to set options

    // Store the tag attributes used to restore html5 element
    this.tagAttributes = tag && Dom.getElAttributes(tag);

    // Update current language
    this.language(this.options_.language);

    // Update Supported Languages
    if (options.languages) {
      (function () {
        // Normalise player option languages to lowercase
        var languagesToLower = {};

        Object.getOwnPropertyNames(options.languages).forEach(function (name) {
          languagesToLower[name.toLowerCase()] = options.languages[name];
        });
        _this.languages_ = languagesToLower;
      })();
    } else {
      this.languages_ = Player.prototype.options_.languages;
    }

    // Cache for video property values.
    this.cache_ = {};

    // Set poster
    this.poster_ = options.poster || '';

    // Set controls
    this.controls_ = !!options.controls;

    // Original tag settings stored in options
    // now remove immediately so native controls don't flash.
    // May be turned back on by HTML5 tech if nativeControlsForTouch is true
    tag.controls = false;

    /*
     * Store the internal state of scrubbing
     *
     * @private
     * @return {Boolean} True if the user is scrubbing
     */
    this.scrubbing_ = false;

    this.el_ = this.createEl();

    // We also want to pass the original player options to each component and plugin
    // as well so they don't need to reach back into the player for options later.
    // We also need to do another copy of this.options_ so we don't end up with
    // an infinite loop.
    var playerOptionsCopy = _utilsMergeOptionsJs2['default'](this.options_);

    // Load plugins
    if (options.plugins) {
      (function () {
        var plugins = options.plugins;

        Object.getOwnPropertyNames(plugins).forEach(function (name) {
          if (typeof this[name] === 'function') {
            this[name](plugins[name]);
          } else {
            _utilsLogJs2['default'].error('Unable to find plugin:', name);
          }
        }, _this);
      })();
    }

    this.options_.playerOptions = playerOptionsCopy;

    this.initChildren();

    // Set isAudio based on whether or not an audio tag was used
    this.isAudio(tag.nodeName.toLowerCase() === 'audio');

    // Update controls className. Can't do this when the controls are initially
    // set because the element doesn't exist yet.
    if (this.controls()) {
      this.addClass('vjs-controls-enabled');
    } else {
      this.addClass('vjs-controls-disabled');
    }

    // Set ARIA label and region role depending on player type
    this.el_.setAttribute('role', 'region');
    if (this.isAudio()) {
      this.el_.setAttribute('aria-label', 'audio player');
    } else {
      this.el_.setAttribute('aria-label', 'video player');
    }

    if (this.isAudio()) {
      this.addClass('vjs-audio');
    }

    if (this.flexNotSupported_()) {
      this.addClass('vjs-no-flex');
    }

    // TODO: Make this smarter. Toggle user state between touching/mousing
    // using events, since devices can have both touch and mouse events.
    // if (browser.TOUCH_ENABLED) {
    //   this.addClass('vjs-touch-enabled');
    // }

    // iOS Safari has broken hover handling
    if (!browser.IS_IOS) {
      this.addClass('vjs-workinghover');
    }

    // Make player easily findable by ID
    Player.players[this.id_] = this;

    // When the player is first initialized, trigger activity so components
    // like the control bar show themselves if needed
    this.userActive(true);
    this.reportUserActivity();
    this.listenForUserActivity_();

    this.on('fullscreenchange', this.handleFullscreenChange_);
    this.on('stageclick', this.handleStageClick_);
  }

  /*
   * Global player list
   *
   * @type {Object}
   */

  /**
   * Destroys the video player and does any necessary cleanup
   * ```js
   *     myPlayer.dispose();
   * ```
   * This is especially helpful if you are dynamically adding and removing videos
   * to/from the DOM.
   *
   * @method dispose
   */

  Player.prototype.dispose = function dispose() {
    this.trigger('dispose');
    // prevent dispose from being called twice
    this.off('dispose');

    if (this.styleEl_ && this.styleEl_.parentNode) {
      this.styleEl_.parentNode.removeChild(this.styleEl_);
    }

    // Kill reference to this player
    Player.players[this.id_] = null;
    if (this.tag && this.tag.player) {
      this.tag.player = null;
    }
    if (this.el_ && this.el_.player) {
      this.el_.player = null;
    }

    if (this.tech_) {
      this.tech_.dispose();
    }

    _Component.prototype.dispose.call(this);
  };

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  Player.prototype.createEl = function createEl() {
    var el = this.el_ = _Component.prototype.createEl.call(this, 'div');
    var tag = this.tag;

    // Remove width/height attrs from tag so CSS can make it 100% width/height
    tag.removeAttribute('width');
    tag.removeAttribute('height');

    // Copy over all the attributes from the tag, including ID and class
    // ID will now reference player box, not the video tag
    var attrs = Dom.getElAttributes(tag);

    Object.getOwnPropertyNames(attrs).forEach(function (attr) {
      // workaround so we don't totally break IE7
      // http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7
      if (attr === 'class') {
        el.className = attrs[attr];
      } else {
        el.setAttribute(attr, attrs[attr]);
      }
    });

    // Update tag id/class for use as HTML5 playback tech
    // Might think we should do this after embedding in container so .vjs-tech class
    // doesn't flash 100% width/height, but class only applies with .video-js parent
    tag.playerId = tag.id;
    tag.id += '_html5_api';
    tag.className = 'vjs-tech';

    // Make player findable on elements
    tag.player = el.player = this;
    // Default state of video is paused
    this.addClass('vjs-paused');

    // Add a style element in the player that we'll use to set the width/height
    // of the player in a way that's still overrideable by CSS, just like the
    // video element
    if (_globalWindow2['default'].VIDEOJS_NO_DYNAMIC_STYLE !== true) {
      this.styleEl_ = stylesheet.createStyleElement('vjs-styles-dimensions');
      var defaultsStyleEl = Dom.$('.vjs-styles-defaults');
      var head = Dom.$('head');
      head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
    }

    // Pass in the width/height/aspectRatio options which will update the style el
    this.width(this.options_.width);
    this.height(this.options_.height);
    this.fluid(this.options_.fluid);
    this.aspectRatio(this.options_.aspectRatio);

    // Hide any links within the video/audio tag, because IE doesn't hide them completely.
    var links = tag.getElementsByTagName('a');
    for (var i = 0; i < links.length; i++) {
      var linkEl = links.item(i);
      Dom.addElClass(linkEl, 'vjs-hidden');
      linkEl.setAttribute('hidden', 'hidden');
    }

    // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
    // keep track of the original for later so we can know if the source originally failed
    tag.initNetworkState_ = tag.networkState;

    // Wrap video tag in div (el/box) container
    if (tag.parentNode) {
      tag.parentNode.insertBefore(el, tag);
    }

    // insert the tag as the first child of the player element
    // then manually add it to the children array so that this.addChild
    // will work properly for other components
    Dom.insertElFirst(tag, el); // Breaks iPhone, fixed in HTML5 setup.
    this.children_.unshift(tag);

    this.el_ = el;

    return el;
  };

  /**
   * Get/set player width
   *
   * @param {Number=} value Value for width
   * @return {Number} Width when getting
   * @method width
   */

  Player.prototype.width = function width(value) {
    return this.dimension('width', value);
  };

  /**
   * Get/set player height
   *
   * @param {Number=} value Value for height
   * @return {Number} Height when getting
   * @method height
   */

  Player.prototype.height = function height(value) {
    return this.dimension('height', value);
  };

  /**
   * Get/set dimension for player
   *
   * @param {String} dimension Either width or height
   * @param {Number=} value Value for dimension
   * @return {Component}
   * @method dimension
   */

  Player.prototype.dimension = function dimension(_dimension, value) {
    var privDimension = _dimension + '_';

    if (value === undefined) {
      return this[privDimension] || 0;
    }

    if (value === '') {
      // If an empty string is given, reset the dimension to be automatic
      this[privDimension] = undefined;
    } else {
      var parsedVal = parseFloat(value);

      if (isNaN(parsedVal)) {
        _utilsLogJs2['default'].error('Improper value "' + value + '" supplied for for ' + _dimension);
        return this;
      }

      this[privDimension] = parsedVal;
    }

    this.updateStyleEl_();
    return this;
  };

  /**
   * Add/remove the vjs-fluid class
   *
   * @param {Boolean} bool Value of true adds the class, value of false removes the class
   * @method fluid
   */

  Player.prototype.fluid = function fluid(bool) {
    if (bool === undefined) {
      return !!this.fluid_;
    }

    this.fluid_ = !!bool;

    if (bool) {
      this.addClass('vjs-fluid');
    } else {
      this.removeClass('vjs-fluid');
    }
  };

  /**
   * Get/Set the aspect ratio
   *
   * @param {String=} ratio Aspect ratio for player
   * @return aspectRatio
   * @method aspectRatio
   */

  Player.prototype.aspectRatio = function aspectRatio(ratio) {
    if (ratio === undefined) {
      return this.aspectRatio_;
    }

    // Check for width:height format
    if (!/^\d+\:\d+$/.test(ratio)) {
      throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
    }
    this.aspectRatio_ = ratio;

    // We're assuming if you set an aspect ratio you want fluid mode,
    // because in fixed mode you could calculate width and height yourself.
    this.fluid(true);

    this.updateStyleEl_();
  };

  /**
   * Update styles of the player element (height, width and aspect ratio)
   *
   * @method updateStyleEl_
   */

  Player.prototype.updateStyleEl_ = function updateStyleEl_() {
    if (_globalWindow2['default'].VIDEOJS_NO_DYNAMIC_STYLE === true) {
      var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
      var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
      var techEl = this.tech_ && this.tech_.el();

      if (techEl) {
        if (_width >= 0) {
          techEl.width = _width;
        }
        if (_height >= 0) {
          techEl.height = _height;
        }
      }

      return;
    }

    var width = undefined;
    var height = undefined;
    var aspectRatio = undefined;
    var idClass = undefined;

    // The aspect ratio is either used directly or to calculate width and height.
    if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
      // Use any aspectRatio that's been specifically set
      aspectRatio = this.aspectRatio_;
    } else if (this.videoWidth()) {
      // Otherwise try to get the aspect ratio from the video metadata
      aspectRatio = this.videoWidth() + ':' + this.videoHeight();
    } else {
      // Or use a default. The video element's is 2:1, but 16:9 is more common.
      aspectRatio = '16:9';
    }

    // Get the ratio as a decimal we can use to calculate dimensions
    var ratioParts = aspectRatio.split(':');
    var ratioMultiplier = ratioParts[1] / ratioParts[0];

    if (this.width_ !== undefined) {
      // Use any width that's been specifically set
      width = this.width_;
    } else if (this.height_ !== undefined) {
      // Or calulate the width from the aspect ratio if a height has been set
      width = this.height_ / ratioMultiplier;
    } else {
      // Or use the video's metadata, or use the video el's default of 300
      width = this.videoWidth() || 300;
    }

    if (this.height_ !== undefined) {
      // Use any height that's been specifically set
      height = this.height_;
    } else {
      // Otherwise calculate the height from the ratio and the width
      height = width * ratioMultiplier;
    }

    // Ensure the CSS class is valid by starting with an alpha character
    if (/^[^a-zA-Z]/.test(this.id())) {
      idClass = 'dimensions-' + this.id();
    } else {
      idClass = this.id() + '-dimensions';
    }

    // Ensure the right class is still on the player for the style element
    this.addClass(idClass);

    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    ');
  };

  /**
   * Load the Media Playback Technology (tech)
   * Load/Create an instance of playback technology including element and API methods
   * And append playback element in player div.
   *
   * @param {String} techName Name of the playback technology
   * @param {String} source Video source
   * @method loadTech_
   * @private
   */

  Player.prototype.loadTech_ = function loadTech_(techName, source) {

    // Pause and remove current playback technology
    if (this.tech_) {
      this.unloadTech_();
    }

    // get rid of the HTML5 video tag as soon as we are using another tech
    if (techName !== 'Html5' && this.tag) {
      _techTechJs2['default'].getTech('Html5').disposeMediaElement(this.tag);
      this.tag.player = null;
      this.tag = null;
    }

    this.techName_ = techName;

    // Turn off API access because we're loading a new tech that might load asynchronously
    this.isReady_ = false;

    // Grab tech-specific options from player options and add source and parent element to use.
    var techOptions = _objectAssign2['default']({
      'nativeControlsForTouch': this.options_.nativeControlsForTouch,
      'source': source,
      'playerId': this.id(),
      'techId': this.id() + '_' + techName + '_api',
      'videoTracks': this.videoTracks_,
      'textTracks': this.textTracks_,
      'audioTracks': this.audioTracks_,
      'autoplay': this.options_.autoplay,
      'playsinline': this.options_.playsinline,
      'controls': this.options_.controls,
      'preload': this.options_.preload,
      'loop': this.options_.loop,
      'muted': this.options_.muted,
      'poster': this.poster(),
      'language': this.language(),
      'vtt.js': this.options_['vtt.js']
    }, this.options_[techName.toLowerCase()]);

    if (this.tag) {
      techOptions.tag = this.tag;
    }

    if (source) {
      this.currentType_ = source.type;
      if (source.src === this.cache_.src && this.cache_.currentTime > 0) {
        techOptions.startTime = this.cache_.currentTime;
      }

      this.cache_.src = source.src;
    }

    // Initialize tech instance
    var techComponent = _techTechJs2['default'].getTech(techName);
    // Support old behavior of techs being registered as components.
    // Remove once that deprecated behavior is removed.
    if (!techComponent) {
      techComponent = _componentJs2['default'].getComponent(techName);
    }
    this.tech_ = new techComponent(techOptions);

    // player.triggerReady is always async, so don't need this to be async
    this.tech_.ready(Fn.bind(this, this.handleTechReady_), true);

    _tracksTextTrackListConverterJs2['default'].jsonToTextTracks(this.textTracksJson_ || [], this.tech_);

    // Listen to all HTML5-defined events and trigger them on the player
    this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
    this.on(this.tech_, 'waiting', this.handleTechWaiting_);
    this.on(this.tech_, 'canplay', this.handleTechCanPlay_);
    this.on(this.tech_, 'canplaythrough', this.handleTechCanPlayThrough_);
    this.on(this.tech_, 'playing', this.handleTechPlaying_);
    this.on(this.tech_, 'ended', this.handleTechEnded_);
    this.on(this.tech_, 'seeking', this.handleTechSeeking_);
    this.on(this.tech_, 'seeked', this.handleTechSeeked_);
    this.on(this.tech_, 'play', this.handleTechPlay_);
    this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
    this.on(this.tech_, 'pause', this.handleTechPause_);
    this.on(this.tech_, 'progress', this.handleTechProgress_);
    this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
    this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
    this.on(this.tech_, 'error', this.handleTechError_);
    this.on(this.tech_, 'suspend', this.handleTechSuspend_);
    this.on(this.tech_, 'abort', this.handleTechAbort_);
    this.on(this.tech_, 'emptied', this.handleTechEmptied_);
    this.on(this.tech_, 'stalled', this.handleTechStalled_);
    this.on(this.tech_, 'loadedmetadata', this.handleTechLoadedMetaData_);
    this.on(this.tech_, 'loadeddata', this.handleTechLoadedData_);
    this.on(this.tech_, 'timeupdate', this.handleTechTimeUpdate_);
    this.on(this.tech_, 'ratechange', this.handleTechRateChange_);
    this.on(this.tech_, 'volumechange', this.handleTechVolumeChange_);
    this.on(this.tech_, 'texttrackchange', this.handleTechTextTrackChange_);
    this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
    this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);

    // Add the tech element in the DOM if it was not already there
    // Make sure to not insert the original video element if using Html5
    if (this.tech_.el().parentNode !== this.el() && (techName !== 'Html5' || !this.tag)) {
      Dom.insertElFirst(this.tech_.el(), this.el());
    }

    // Get rid of the original video tag reference after the first tech is loaded
    if (this.tag) {
      this.tag.player = null;
      this.tag = null;
    }
  };

  /**
   * Unload playback technology
   *
   * @method unloadTech_
   * @private
   */

  Player.prototype.unloadTech_ = function unloadTech_() {
    // Save the current text tracks so that we can reuse the same text tracks with the next tech
    this.videoTracks_ = this.videoTracks();
    this.textTracks_ = this.textTracks();
    this.audioTracks_ = this.audioTracks();
    this.textTracksJson_ = _tracksTextTrackListConverterJs2['default'].textTracksToJson(this.tech_);

    this.isReady_ = false;

    this.tech_.dispose();

    this.tech_ = false;
  };

  /**
   * Return a reference to the current tech.
   * It will only return a reference to the tech if given an object with the
   * `IWillNotUseThisInPlugins` property on it. This is try and prevent misuse
   * of techs by plugins.
   *
   * @param {Object}
   * @return {Object} The Tech
   * @method tech
   */

  Player.prototype.tech = function tech(safety) {
    if (safety && safety.IWillNotUseThisInPlugins) {
      return this.tech_;
    }
    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    ';
    _globalWindow2['default'].alert(errorText);
    throw new Error(errorText);
  };

  /**
   * Set up click and touch listeners for the playback element
   *
   * On desktops, a click on the video itself will toggle playback,
   * on a mobile device a click on the video toggles controls.
   * (toggling controls is done by toggling the user state between active and
   * inactive)
   * A tap can signal that a user has become active, or has become inactive
   * e.g. a quick tap on an iPhone movie should reveal the controls. Another
   * quick tap should hide them again (signaling the user is in an inactive
   * viewing state)
   * In addition to this, we still want the user to be considered inactive after
   * a few seconds of inactivity.
   * Note: the only part of iOS interaction we can't mimic with this setup
   * is a touch and hold on the video element counting as activity in order to
   * keep the controls showing, but that shouldn't be an issue. A touch and hold
   * on any controls will still keep the user active
   *
   * @private
   * @method addTechControlsListeners_
   */

  Player.prototype.addTechControlsListeners_ = function addTechControlsListeners_() {
    // Make sure to remove all the previous listeners in case we are called multiple times.
    this.removeTechControlsListeners_();

    // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
    // trigger mousedown/up.
    // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
    // Any touch events are set to block the mousedown event from happening
    this.on(this.tech_, 'mousedown', this.handleTechClick_);
    this.on(this.tech_, 'dblclick', this.handleTechDblClick_);

    // If the controls were hidden we don't want that to change without a tap event
    // so we'll check if the controls were already showing before reporting user
    // activity
    this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
    this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
    this.on(this.tech_, 'touchend', this.handleTechTouchEnd_);

    // The tap listener needs to come after the touchend listener because the tap
    // listener cancels out any reportedUserActivity when setting userActive(false)
    this.on(this.tech_, 'tap', this.handleTechTap_);
  };

  /**
   * Remove the listeners used for click and tap controls. This is needed for
   * toggling to controls disabled, where a tap/touch should do nothing.
   *
   * @method removeTechControlsListeners_
   * @private
   */

  Player.prototype.removeTechControlsListeners_ = function removeTechControlsListeners_() {
    // We don't want to just use `this.off()` because there might be other needed
    // listeners added by techs that extend this.
    this.off(this.tech_, 'tap', this.handleTechTap_);
    this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
    this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
    this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
    this.off(this.tech_, 'mousedown', this.handleTechClick_);
    this.off(this.tech_, 'dblclick', this.handleTechDblClick_);
  };

  /**
   * Player waits for the tech to be ready
   *
   * @method handleTechReady_
   * @private
   */

  Player.prototype.handleTechReady_ = function handleTechReady_() {
    this.usingNativeControls(this.techGet_('controls'));

    if (this.controls() && !this.usingNativeControls()) {
      this.addTechControlsListeners_();
    }

    this.triggerReady();

    // Keep the same volume as before
    if (this.cache_.volume) {
      this.techCall_('setVolume', this.cache_.volume);
    }

    // Look if the tech found a higher resolution poster while loading
    this.handleTechPosterChange_();

    // Update the duration if available
    this.handleTechDurationChange_();

    // Chrome and Safari both have issues with autoplay.
    // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work.
    // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays)
    // This fixes both issues. Need to wait for API, so it updates displays correctly
    if (this.src() && this.tag && this.options_.autoplay && this.paused()) {
      delete this.tag.poster; // Chrome Fix. Fixed in Chrome v16.
      this.play();
    }
  };

  /**
   * Fired when the user agent begins looking for media data
   *
   * @private
   * @method handleTechLoadStart_
   */

  Player.prototype.handleTechLoadStart_ = function handleTechLoadStart_() {
    // TODO: Update to use `emptied` event instead. See #1277.

    this.removeClass('vjs-ended');
    this.removeClass('vjs-seeking');

    // reset the error state
    this.error(null);

    // If it's already playing we want to trigger a firstplay event now.
    // The firstplay event relies on both the play and loadstart events
    // which can happen in any order for a new source
    if (!this.paused()) {
      this.trigger('loadstart');
      this.trigger('firstplay');
    } else {
      // reset the hasStarted state
      this.hasStarted(false);
      this.trigger('loadstart');
    }
  };

  /**
   * Add/remove the vjs-has-started class
   *
   * @param {Boolean} hasStarted The value of true adds the class the value of false remove the class
   * @return {Boolean} Boolean value if has started
   * @private
   * @method hasStarted
   */

  Player.prototype.hasStarted = function hasStarted(_hasStarted) {
    if (_hasStarted !== undefined) {
      // only update if this is a new value
      if (this.hasStarted_ !== _hasStarted) {
        this.hasStarted_ = _hasStarted;
        if (_hasStarted) {
          this.addClass('vjs-has-started');
          // trigger the firstplay event if this newly has played
          this.trigger('firstplay');
        } else {
          this.removeClass('vjs-has-started');
        }
      }
      return this;
    }
    return !!this.hasStarted_;
  };

  /**
   * Fired whenever the media begins or resumes playback
   *
   * @private
   * @method handleTechPlay_
   */

  Player.prototype.handleTechPlay_ = function handleTechPlay_() {
    this.removeClass('vjs-ended');
    this.removeClass('vjs-paused');
    this.addClass('vjs-playing');

    // hide the poster when the user hits play
    // https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play
    this.hasStarted(true);

    this.trigger('play');
  };

  /**
   * Fired whenever the media begins waiting
   *
   * @private
   * @method handleTechWaiting_
   */

  Player.prototype.handleTechWaiting_ = function handleTechWaiting_() {
    var _this2 = this;

    this.addClass('vjs-waiting');
    this.trigger('waiting');
    this.one('timeupdate', function () {
      return _this2.removeClass('vjs-waiting');
    });
  };

  /**
   * A handler for events that signal that waiting has ended
   * which is not consistent between browsers. See #1351
   *
   * @private
   * @method handleTechCanPlay_
   */

  Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() {
    this.removeClass('vjs-waiting');
    this.trigger('canplay');
  };

  /**
   * A handler for events that signal that waiting has ended
   * which is not consistent between browsers. See #1351
   *
   * @private
   * @method handleTechCanPlayThrough_
   */

  Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
    this.removeClass('vjs-waiting');
    this.trigger('canplaythrough');
  };

  /**
   * A handler for events that signal that waiting has ended
   * which is not consistent between browsers. See #1351
   *
   * @private
   * @method handleTechPlaying_
   */

  Player.prototype.handleTechPlaying_ = function handleTechPlaying_() {
    this.removeClass('vjs-waiting');
    this.trigger('playing');
  };

  /**
   * Fired whenever the player is jumping to a new time
   *
   * @private
   * @method handleTechSeeking_
   */

  Player.prototype.handleTechSeeking_ = function handleTechSeeking_() {
    this.addClass('vjs-seeking');
    this.trigger('seeking');
  };

  /**
   * Fired when the player has finished jumping to a new time
   *
   * @private
   * @method handleTechSeeked_
   */

  Player.prototype.handleTechSeeked_ = function handleTechSeeked_() {
    this.removeClass('vjs-seeking');
    this.trigger('seeked');
  };

  /**
   * Fired the first time a video is played
   * Not part of the HLS spec, and we're not sure if this is the best
   * implementation yet, so use sparingly. If you don't have a reason to
   * prevent playback, use `myPlayer.one('play');` instead.
   *
   * @private
   * @method handleTechFirstPlay_
   */

  Player.prototype.handleTechFirstPlay_ = function handleTechFirstPlay_() {
    //If the first starttime attribute is specified
    //then we will start at the given offset in seconds
    if (this.options_.starttime) {
      this.currentTime(this.options_.starttime);
    }

    this.addClass('vjs-has-started');
    this.trigger('firstplay');
  };

  /**
   * Fired whenever the media has been paused
   *
   * @private
   * @method handleTechPause_
   */

  Player.prototype.handleTechPause_ = function handleTechPause_() {
    this.removeClass('vjs-playing');
    this.addClass('vjs-paused');
    this.trigger('pause');
  };

  /**
   * Fired while the user agent is downloading media data
   *
   * @private
   * @method handleTechProgress_
   */

  Player.prototype.handleTechProgress_ = function handleTechProgress_() {
    this.trigger('progress');
  };

  /**
   * Fired when the end of the media resource is reached (currentTime == duration)
   *
   * @private
   * @method handleTechEnded_
   */

  Player.prototype.handleTechEnded_ = function handleTechEnded_() {
    this.addClass('vjs-ended');
    if (this.options_.loop) {
      this.currentTime(0);
      this.play();
    } else if (!this.paused()) {
      this.pause();
    }

    this.trigger('ended');
  };

  /**
   * Fired when the duration of the media resource is first known or changed
   *
   * @private
   * @method handleTechDurationChange_
   */

  Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() {
    this.duration(this.techGet_('duration'));
  };

  Player.prototype.toggleFullscreen_ = function toggleFullscreen_() {
    if (this.isFullscreen()) {
      this.exitFullscreen();
    } else {
      this.requestFullscreen();
    }
  };

  /**
   * Handle a click on the media element to play/pause
   *
   * @param {Object=} event Event object
   * @private
   * @method handleTechClick_
   */

  Player.prototype.handleTechClick_ = function handleTechClick_(event) {
    // We're using mousedown to detect clicks thanks to Flash, but mousedown
    // will also be triggered with right-clicks, so we need to prevent that
    // When controls are disabled a click should not toggle playback because
    // the click is considered a control
    if (event.type === 'mousedown' && event.button !== 0 || !this.controls()) {
      return;
    }
    if (this.techClickTimeout_) {
      this.clearTimeout(this.techClickTimeout_);
      this.techClickTimeout_ = null;
      // IE11 doesn't allow to enter full screen on mousedown
      if (_fullscreenApiJs2['default'].requestFullscreen !== 'msRequestFullscreen') this.toggleFullscreen_();
      return;
    }
    this.techClickTimeout_ = this.setTimeout(function () {
      this.techClickTimeout_ = null;
      if (this.paused()) {
        this.play();
      } else {
        this.pause();
      }
      // required by hola-video-skin to show pause/play animation
      this.trigger('videoclick');
    }, 300);
  };

  Player.prototype.handleTechDblClick_ = function handleTechDblClick_() {
    // IE11 doesn't allow to enter full screen on mousedown
    if (_fullscreenApiJs2['default'].requestFullscreen === 'msRequestFullscreen') this.toggleFullscreen_();
  };

  /**
   * Handle a tap on the media element. It will toggle the user
   * activity state, which hides and shows the controls.
   *
   * @private
   * @method handleTechTap_
   */

  Player.prototype.handleTechTap_ = function handleTechTap_(event) {
    if (this.userActive() || this.paused()) {
      this.handleTechClick_(event);
    } else {
      this.userActive(true);
    }
  };

  /**
   * Handle touch to start
   *
   * @private
   * @method handleTechTouchStart_
   */

  Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() {
    this.userWasActive = this.userActive();
  };

  /**
   * Handle touch to move
   *
   * @private
   * @method handleTechTouchMove_
   */

  Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() {
    if (this.userWasActive) {
      this.reportUserActivity();
    }
  };

  /**
   * Handle touch to end
   *
   * @private
   * @method handleTechTouchEnd_
   */

  Player.prototype.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
    // Stop the mouse events from also happening
    event.preventDefault();
  };

  /**
   * Fired when the player switches in or out of fullscreen mode
   *
   * @private
   * @method handleFullscreenChange_
   */

  Player.prototype.handleFullscreenChange_ = function handleFullscreenChange_() {
    if (this.isFullscreen()) {
      this.addClass('vjs-fullscreen');
    } else {
      this.removeClass('vjs-fullscreen');
    }
  };

  /**
   * native click events on the SWF aren't triggered on IE11, Win8.1RT
   * use stageclick events triggered from inside the SWF instead
   *
   * @private
   * @method handleStageClick_
   */

  Player.prototype.handleStageClick_ = function handleStageClick_() {
    this.reportUserActivity();
  };

  /**
   * Handle Tech Fullscreen Change
   *
   * @private
   * @method handleTechFullscreenChange_
   */

  Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
    if (data) {
      this.isFullscreen(data.isFullscreen);
    }
    this.trigger('fullscreenchange');
  };

  /**
   * Fires when an error occurred during the loading of an audio/video
   *
   * @private
   * @method handleTechError_
   */

  Player.prototype.handleTechError_ = function handleTechError_() {
    var error = this.tech_.error();
    this.error(error && error.code);
  };

  /**
   * Fires when the browser is intentionally not getting media data
   *
   * @private
   * @method handleTechSuspend_
   */

  Player.prototype.handleTechSuspend_ = function handleTechSuspend_() {
    this.trigger('suspend');
  };

  /**
   * Fires when the loading of an audio/video is aborted
   *
   * @private
   * @method handleTechAbort_
   */

  Player.prototype.handleTechAbort_ = function handleTechAbort_() {
    this.trigger('abort');
  };

  /**
   * Fires when the current playlist is empty
   *
   * @private
   * @method handleTechEmptied_
   */

  Player.prototype.handleTechEmptied_ = function handleTechEmptied_() {
    this.trigger('emptied');
  };

  /**
   * Fires when the browser is trying to get media data, but data is not available
   *
   * @private
   * @method handleTechStalled_
   */

  Player.prototype.handleTechStalled_ = function handleTechStalled_() {
    this.trigger('stalled');
  };

  /**
   * Fires when the browser has loaded meta data for the audio/video
   *
   * @private
   * @method handleTechLoadedMetaData_
   */

  Player.prototype.handleTechLoadedMetaData_ = function handleTechLoadedMetaData_() {
    this.trigger('loadedmetadata');
  };

  /**
   * Fires when the browser has loaded the current frame of the audio/video
   *
   * @private
   * @method handleTechLoadedData_
   */

  Player.prototype.handleTechLoadedData_ = function handleTechLoadedData_() {
    this.trigger('loadeddata');
  };

  /**
   * Fires when the current playback position has changed
   *
   * @private
   * @method handleTechTimeUpdate_
   */

  Player.prototype.handleTechTimeUpdate_ = function handleTechTimeUpdate_() {
    this.trigger('timeupdate');
  };

  /**
   * Fires when the playing speed of the audio/video is changed
   *
   * @private
   * @method handleTechRateChange_
   */

  Player.prototype.handleTechRateChange_ = function handleTechRateChange_() {
    this.trigger('ratechange');
  };

  /**
   * Fires when the volume has been changed
   *
   * @private
   * @method handleTechVolumeChange_
   */

  Player.prototype.handleTechVolumeChange_ = function handleTechVolumeChange_() {
    this.trigger('volumechange');
  };

  /**
   * Fires when the text track has been changed
   *
   * @private
   * @method handleTechTextTrackChange_
   */

  Player.prototype.handleTechTextTrackChange_ = function handleTechTextTrackChange_() {
    this.trigger('texttrackchange');
  };

  /**
   * Get object for cached values.
   *
   * @return {Object}
   * @method getCache
   */

  Player.prototype.getCache = function getCache() {
    return this.cache_;
  };

  /**
   * Pass values to the playback tech
   *
   * @param {String=} method Method
   * @param {Object=} arg Argument
   * @private
   * @method techCall_
   */

  Player.prototype.techCall_ = function techCall_(method, arg) {
    // Allows to rewrite calls (e.g. to redirect play/pause calls to Ads plugin)
    if (this.hola && this.hola.tech_call && this.hola.tech_call(method, arg)) {
      return;
    }

    // If it's not ready yet, call method when it is
    if (this.tech_ && !this.tech_.isReady_) {
      this.tech_.ready(function () {
        this[method](arg);
      }, true);

      // Otherwise call method now
    } else {
        try {
          if (this.tech_) {
            this.tech_[method](arg);
          }
        } catch (e) {
          _utilsLogJs2['default'](e);
          throw e;
        }
      }
  };

  /**
   * Get calls can't wait for the tech, and sometimes don't need to.
   *
   * @param {String} method Tech method
   * @return {Method}
   * @private
   * @method techGet_
   */

  Player.prototype.techGet_ = function techGet_(method) {
    if (this.tech_ && this.tech_.isReady_) {

      // Flash likes to die and reload when you hide or reposition it.
      // In these cases the object methods go away and we get errors.
      // When that happens we'll catch the errors and inform tech that it's not ready any more.
      try {
        return this.tech_[method]();
      } catch (e) {
        // When building additional tech libs, an expected method may not be defined yet
        if (this.tech_[method] === undefined) {
          _utilsLogJs2['default']('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e);
        } else {
          // When a method isn't available on the object it throws a TypeError
          if (e.name === 'TypeError') {
            _utilsLogJs2['default']('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e);
            this.tech_.isReady_ = false;
          } else {
            _utilsLogJs2['default'](e);
          }
        }
        throw e;
      }
    }

    return;
  };

  /**
   * start media playback
   * ```js
   *     myPlayer.play();
   * ```
   *
   * @return {Player} self
   * @method play
   */

  Player.prototype.play = function play() {
    this.techCall_('play');
    return this;
  };

  /**
   * Pause the video playback
   * ```js
   *     myPlayer.pause();
   * ```
   *
   * @return {Player} self
   * @method pause
   */

  Player.prototype.pause = function pause() {
    this.techCall_('pause');
    return this;
  };

  /**
   * Check if the player is paused
   * ```js
   *     var isPaused = myPlayer.paused();
   *     var isPlaying = !myPlayer.paused();
   * ```
   *
   * @return {Boolean} false if the media is currently playing, or true otherwise
   * @method paused
   */

  Player.prototype.paused = function paused() {
    // The initial state of paused should be true (in Safari it's actually false)
    return this.techGet_('paused') === false ? false : true;
  };

  /**
   * Returns whether or not the user is "scrubbing". Scrubbing is when the user
   * has clicked the progress bar handle and is dragging it along the progress bar.
   *
   * @param  {Boolean} isScrubbing   True/false the user is scrubbing
   * @return {Boolean}               The scrubbing status when getting
   * @return {Object}                The player when setting
   * @method scrubbing
   */

  Player.prototype.scrubbing = function scrubbing(isScrubbing) {
    if (isScrubbing !== undefined) {
      this.scrubbing_ = !!isScrubbing;

      if (isScrubbing) {
        this.addClass('vjs-scrubbing');
      } else {
        this.removeClass('vjs-scrubbing');
      }

      return this;
    }

    return this.scrubbing_;
  };

  /**
   * Get or set the current time (in seconds)
   * ```js
   *     // get
   *     var whereYouAt = myPlayer.currentTime();
   *     // set
   *     myPlayer.currentTime(120); // 2 minutes into the video
   * ```
   *
   * @param  {Number|String=} seconds The time to seek to
   * @return {Number}        The time in seconds, when not setting
   * @return {Player}    self, when the current time is set
   * @method currentTime
   */

  Player.prototype.currentTime = function currentTime(seconds) {
    if (seconds !== undefined) {

      this.techCall_('setCurrentTime', seconds);

      return this;
    }

    // cache last currentTime and return. default to 0 seconds
    //
    // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
    // currentTime when scrubbing, but may not provide much performance benefit afterall.
    // Should be tested. Also something has to read the actual current time or the cache will
    // never get updated.
    return this.cache_.currentTime = this.techGet_('currentTime') || 0;
  };

  /**
   * Get the length in time of the video in seconds
   * ```js
   *     var lengthOfVideo = myPlayer.duration();
   * ```
   * **NOTE**: The video must have started loading before the duration can be
   * known, and in the case of Flash, may not be known until the video starts
   * playing.
   *
   * @param {Number} seconds Duration when setting
   * @return {Number} The duration of the video in seconds when getting
   * @method duration
   */

  Player.prototype.duration = function duration(seconds) {
    if (seconds === undefined) {
      return this.cache_.duration || 0;
    }

    seconds = parseFloat(seconds) || 0;

    // Standardize on Inifity for signaling video is live
    if (seconds < 0) {
      seconds = Infinity;
    }

    if (seconds !== this.cache_.duration) {
      // Cache the last set value for optimized scrubbing (esp. Flash)
      this.cache_.duration = seconds;

      if (seconds === Infinity) {
        this.addClass('vjs-live');
      } else {
        this.removeClass('vjs-live');
      }

      this.trigger('durationchange');
    }

    return this;
  };

  /**
   * Calculates how much time is left.
   * ```js
   *     var timeLeft = myPlayer.remainingTime();
   * ```
   * Not a native video element function, but useful
   *
   * @return {Number} The time remaining in seconds
   * @method remainingTime
   */

  Player.prototype.remainingTime = function remainingTime() {
    return this.duration() - this.currentTime();
  };

  // http://dev.w3.org/html5/spec/video.html#dom-media-buffered
  // Buffered returns a timerange object.
  // Kind of like an array of portions of the video that have been downloaded.

  /**
   * Get a TimeRange object with the times of the video that have been downloaded
   * If you just want the percent of the video that's been downloaded,
   * use bufferedPercent.
   * ```js
   *     // Number of different ranges of time have been buffered. Usually 1.
   *     numberOfRanges = bufferedTimeRange.length,
   *     // Time in seconds when the first range starts. Usually 0.
   *     firstRangeStart = bufferedTimeRange.start(0),
   *     // Time in seconds when the first range ends
   *     firstRangeEnd = bufferedTimeRange.end(0),
   *     // Length in seconds of the first time range
   *     firstRangeLength = firstRangeEnd - firstRangeStart;
   * ```
   *
   * @return {Object} A mock TimeRange object (following HTML spec)
   * @method buffered
   */

  Player.prototype.buffered = function buffered() {
    var buffered = this.techGet_('buffered');

    if (!buffered || !buffered.length) {
      buffered = _utilsTimeRangesJs.createTimeRange(0, 0);
    }

    return buffered;
  };

  /**
   * Get the percent (as a decimal) of the video that's been downloaded
   * ```js
   *     var howMuchIsDownloaded = myPlayer.bufferedPercent();
   * ```
   * 0 means none, 1 means all.
   * (This method isn't in the HTML5 spec, but it's very convenient)
   *
   * @return {Number} A decimal between 0 and 1 representing the percent
   * @method bufferedPercent
   */

  Player.prototype.bufferedPercent = function bufferedPercent() {
    return _utilsBufferJs.bufferedPercent(this.buffered(), this.duration());
  };

  /**
   * Get the ending time of the last buffered time range
   * This is used in the progress bar to encapsulate all time ranges.
   *
   * @return {Number} The end of the last buffered time range
   * @method bufferedEnd
   */

  Player.prototype.bufferedEnd = function bufferedEnd() {
    var buffered = this.buffered(),
        duration = this.duration(),
        end = buffered.end(buffered.length - 1);

    if (end > duration) {
      end = duration;
    }

    return end;
  };

  /**
   * Get or set the current volume of the media
   * ```js
   *     // get
   *     var howLoudIsIt = myPlayer.volume();
   *     // set
   *     myPlayer.volume(0.5); // Set volume to half
   * ```
   * 0 is off (muted), 1.0 is all the way up, 0.5 is half way.
   *
   * @param  {Number} percentAsDecimal The new volume as a decimal percent
   * @return {Number}              The current volume when getting
   * @return {Player}              self when setting
   * @method volume
   */

  Player.prototype.volume = function volume(percentAsDecimal) {
    var vol = undefined;

    if (percentAsDecimal !== undefined) {
      vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal))); // Force value to between 0 and 1
      this.cache_.volume = vol;
      this.techCall_('setVolume', vol);

      return this;
    }

    // Default to 1 when returning current volume.
    vol = parseFloat(this.techGet_('volume'));
    return isNaN(vol) ? 1 : vol;
  };

  /**
   * Get the current muted state, or turn mute on or off
   * ```js
   *     // get
   *     var isVolumeMuted = myPlayer.muted();
   *     // set
   *     myPlayer.muted(true); // mute the volume
   * ```
   *
   * @param  {Boolean=} muted True to mute, false to unmute
   * @return {Boolean} True if mute is on, false if not when getting
   * @return {Player} self when setting mute
   * @method muted
   */

  Player.prototype.muted = function muted(_muted) {
    if (_muted !== undefined) {
      this.techCall_('setMuted', _muted);
      return this;
    }
    return this.techGet_('muted') || false; // Default to false
  };

  // Check if current tech can support native fullscreen
  // (e.g. with built in controls like iOS, so not our flash swf)
  /**
   * Check to see if fullscreen is supported
   *
   * @return {Boolean}
   * @method supportsFullScreen
   */

  Player.prototype.supportsFullScreen = function supportsFullScreen() {
    return this.techGet_('supportsFullScreen') || false;
  };

  /**
   * Check if the player is in fullscreen mode
   * ```js
   *     // get
   *     var fullscreenOrNot = myPlayer.isFullscreen();
   *     // set
   *     myPlayer.isFullscreen(true); // tell the player it's in fullscreen
   * ```
   * NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
   * property and instead document.fullscreenElement is used. But isFullscreen is
   * still a valuable property for internal player workings.
   *
   * @param  {Boolean=} isFS Update the player's fullscreen state
   * @return {Boolean} true if fullscreen false if not when getting
   * @return {Player} self when setting
   * @method isFullscreen
   */

  Player.prototype.isFullscreen = function isFullscreen(isFS) {
    if (isFS !== undefined) {
      this.isFullscreen_ = !!isFS;
      return this;
    }
    return !!this.isFullscreen_;
  };

  /**
   * Increase the size of the video to full screen
   * ```js
   *     myPlayer.requestFullscreen();
   * ```
   * In some browsers, full screen is not supported natively, so it enters
   * "full window mode", where the video fills the browser window.
   * In browsers and devices that support native full screen, sometimes the
   * browser's default controls will be shown, and not the Video.js custom skin.
   * This includes most mobile devices (iOS, Android) and older versions of
   * Safari.
   *
   * @return {Player} self
   * @method requestFullscreen
   */

  Player.prototype.requestFullscreen = function requestFullscreen() {
    var fsApi = _fullscreenApiJs2['default'];

    this.isFullscreen(true);
    var fullscreenDisabled = _globalDocument2['default'][fsApi.fullscreenEnabled] === false;

    if (fsApi.requestFullscreen && !fullscreenDisabled) {
      // the browser supports going fullscreen at the element level so we can
      // take the controls fullscreen as well as the video

      // Trigger fullscreenchange event after change
      // We have to specifically add this each time, and remove
      // when canceling fullscreen. Otherwise if there's multiple
      // players on a page, they would all be reacting to the same fullscreen
      // events
      Events.on(_globalDocument2['default'], fsApi.fullscreenchange, Fn.bind(this, function documentFullscreenChange(e) {
        this.isFullscreen(_globalDocument2['default'][fsApi.fullscreenElement]);

        // If cancelling fullscreen, remove event listener.
        if (this.isFullscreen() === false) {
          Events.off(_globalDocument2['default'], fsApi.fullscreenchange, documentFullscreenChange);
        }

        this.trigger('fullscreenchange');
      }));

      this.el_[fsApi.requestFullscreen]();
    } else if (this.tech_.supportsFullScreen() && !fullscreenDisabled) {
      // we can't take the video.js controls fullscreen but we can go fullscreen
      // with native controls
      this.techCall_('enterFullScreen');
    } else {
      // fullscreen isn't supported so we'll just stretch the video element to
      // fill the viewport
      this.enterFullWindow();
      this.trigger('fullscreenchange');
    }

    return this;
  };

  /**
   * Return the video to its normal size after having been in full screen mode
   * ```js
   *     myPlayer.exitFullscreen();
   * ```
   *
   * @return {Player} self
   * @method exitFullscreen
   */

  Player.prototype.exitFullscreen = function exitFullscreen() {
    var fsApi = _fullscreenApiJs2['default'];
    this.isFullscreen(false);
    var fullscreenDisabled = _globalDocument2['default'][fsApi.fullscreenEnabled] === false;

    // Check for browser element fullscreen support
    if (fsApi.requestFullscreen && !fullscreenDisabled) {
      _globalDocument2['default'][fsApi.exitFullscreen]();
    } else if (this.tech_.supportsFullScreen() && !fullscreenDisabled) {
      this.techCall_('exitFullScreen');
    } else {
      this.exitFullWindow();
      this.trigger('fullscreenchange');
    }

    return this;
  };

  /**
   * When fullscreen isn't supported we can stretch the video container to as wide as the browser will let us.
   *
   * @method enterFullWindow
   */

  Player.prototype.enterFullWindow = function enterFullWindow() {
    this.isFullWindow = true;

    // Storing original doc overflow value to return to when fullscreen is off
    this.docOrigOverflow = _globalDocument2['default'].documentElement.style.overflow;

    // Add listener for esc key to exit fullscreen
    Events.on(_globalDocument2['default'], 'keydown', Fn.bind(this, this.fullWindowOnEscKey));

    // Hide any scroll bars
    _globalDocument2['default'].documentElement.style.overflow = 'hidden';

    // Apply fullscreen styles
    Dom.addElClass(_globalDocument2['default'].body, 'vjs-full-window');

    var parent = this.el_.parentNode;
    while (parent && parent !== _globalDocument2['default'].body) {
      Dom.addElClass(parent, 'vjs-fake-fullscreen-parent');
      parent = parent.parentNode;
    }

    this.trigger('enterFullWindow');
  };

  /**
   * Check for call to either exit full window or full screen on ESC key
   *
   * @param {String} event Event to check for key press
   * @method fullWindowOnEscKey
   */

  Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
    if (event.keyCode === 27) {
      if (this.isFullscreen() === true) {
        this.exitFullscreen();
      } else {
        this.exitFullWindow();
      }
    }
  };

  /**
   * Exit full window
   *
   * @method exitFullWindow
   */

  Player.prototype.exitFullWindow = function exitFullWindow() {
    this.isFullWindow = false;
    Events.off(_globalDocument2['default'], 'keydown', this.fullWindowOnEscKey);

    // Unhide scroll bars.
    _globalDocument2['default'].documentElement.style.overflow = this.docOrigOverflow;

    // Remove fullscreen styles
    Dom.removeElClass(_globalDocument2['default'].body, 'vjs-full-window');

    var parent = this.el_.parentNode;
    while (parent && parent !== _globalDocument2['default'].body) {
      Dom.removeElClass(parent, 'vjs-fake-fullscreen-parent');
      parent = parent.parentNode;
    }

    // Resize the box, controller, and poster to original sizes
    // this.positionAll();
    this.trigger('exitFullWindow');
  };

  /**
   * Check whether the player can play a given mimetype
   *
   * @param {String} type The mimetype to check
   * @return {String} 'probably', 'maybe', or '' (empty string)
   * @method canPlayType
   */

  Player.prototype.canPlayType = function canPlayType(type) {
    var can = undefined;

    // Loop through each playback technology in the options order
    for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
      var techName = _utilsToTitleCaseJs2['default'](j[i]);
      var tech = _techTechJs2['default'].getTech(techName);

      // Support old behavior of techs being registered as components.
      // Remove once that deprecated behavior is removed.
      if (!tech) {
        tech = _componentJs2['default'].getComponent(techName);
      }

      // Check if the current tech is defined before continuing
      if (!tech) {
        _utilsLogJs2['default'].error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
        continue;
      }

      // Check if the browser supports this technology
      if (tech.isSupported()) {
        can = tech.canPlayType(type);

        if (can) {
          return can;
        }
      }
    }

    return '';
  };

  /**
   * Select source based on tech-order or source-order
   * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
   * defaults to tech-order selection
   *
   * @param {Array} sources The sources for a media asset
   * @return {Object|Boolean} Object of source and tech order, otherwise false
   * @method selectSource
   */

  Player.prototype.selectSource = function selectSource(sources) {
    // Get only the techs specified in `techOrder` that exist and are supported by the
    // current platform
    var techs = this.options_.techOrder.map(_utilsToTitleCaseJs2['default']).map(function (techName) {
      // `Component.getComponent(...)` is for support of old behavior of techs
      // being registered as components.
      // Remove once that deprecated behavior is removed.
      return [techName, _techTechJs2['default'].getTech(techName) || _componentJs2['default'].getComponent(techName)];
    }).filter(function (_ref) {
      var techName = _ref[0];
      var tech = _ref[1];

      // Check if the current tech is defined before continuing
      if (tech) {
        // Check if the browser supports this technology
        return tech.isSupported();
      }

      _utilsLogJs2['default'].error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
      return false;
    });

    // Iterate over each `innerArray` element once per `outerArray` element and execute
    // `tester` with both. If `tester` returns a non-falsy value, exit early and return
    // that value.
    var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
      var found = undefined;

      outerArray.some(function (outerChoice) {
        return innerArray.some(function (innerChoice) {
          found = tester(outerChoice, innerChoice);

          if (found) {
            return true;
          }
        });
      });

      return found;
    };

    var foundSourceAndTech = undefined;
    var flip = function flip(fn) {
      return function (a, b) {
        return fn(b, a);
      };
    };
    var finder = function finder(_ref2, source) {
      var techName = _ref2[0];
      var tech = _ref2[1];

      if (tech.canPlaySource(source)) {
        return { source: source, tech: techName };
      }
    };

    // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
    // to select from them based on their priority.
    if (this.options_.sourceOrder) {
      // Source-first ordering
      foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
    } else {
      // Tech-first ordering
      foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
    }

    return foundSourceAndTech || false;
  };

  /**
   * The source function updates the video source
   * There are three types of variables you can pass as the argument.
   * **URL String**: A URL to the the video file. Use this method if you are sure
   * the current playback technology (HTML5/Flash) can support the source you
   * provide. Currently only MP4 files can be used in both HTML5 and Flash.
   * ```js
   *     myPlayer.src("http://www.example.com/path/to/video.mp4");
   * ```
   * **Source Object (or element):* * A javascript object containing information
   * about the source file. Use this method if you want the player to determine if
   * it can support the file using the type information.
   * ```js
   *     myPlayer.src({ type: "video/mp4", src: "http://www.example.com/path/to/video.mp4" });
   * ```
   * **Array of Source Objects:* * To provide multiple versions of the source so
   * that it can be played using HTML5 across browsers you can use an array of
   * source objects. Video.js will detect which version is supported and load that
   * file.
   * ```js
   *     myPlayer.src([
   *       { type: "video/mp4", src: "http://www.example.com/path/to/video.mp4" },
   *       { type: "video/webm", src: "http://www.example.com/path/to/video.webm" },
   *       { type: "video/ogg", src: "http://www.example.com/path/to/video.ogv" }
   *     ]);
   * ```
   *
   * @param  {String|Object|Array=} source The source URL, object, or array of sources
   * @return {String} The current video source when getting
   * @return {String} The player when setting
   * @method src
   */

  Player.prototype.src = function src(source) {
    if (source === undefined) {
      return this.techGet_('src');
    }

    var currentTech = _techTechJs2['default'].getTech(this.techName_);
    // Support old behavior of techs being registered as components.
    // Remove once that deprecated behavior is removed.
    if (!currentTech) {
      currentTech = _componentJs2['default'].getComponent(this.techName_);
    }

    // case: Array of source objects to choose from and pick the best to play
    if (Array.isArray(source)) {
      this.sourceList_(source);

      // case: URL String (http://myvideo...)
    } else if (typeof source === 'string') {
        // create a source object from the string
        this.src({ src: source });

        // case: Source object { src: '', type: '' ... }
      } else if (source instanceof Object) {
          // check if the source has a type and the loaded tech cannot play the source
          // if there's no type we'll just try the current tech
          if (source.type && !currentTech.canPlaySource(source)) {
            // create a source list with the current source and send through
            // the tech loop to check for a compatible technology
            this.sourceList_([source]);
          } else {
            this.cache_.src = source.src;
            this.currentType_ = source.type || '';

            // wait until the tech is ready to set the source
            this.ready(function () {

              // The setSource tech method was added with source handlers
              // so older techs won't support it
              // We need to check the direct prototype for the case where subclasses
              // of the tech do not support source handlers
              if (currentTech.prototype.hasOwnProperty('setSource')) {
                this.techCall_('setSource', source);
              } else {
                this.techCall_('src', source.src);
              }

              if (this.options_.preload === 'auto') {
                this.load();
              }

              if (this.options_.autoplay) {
                this.play();
              }

              // Set the source synchronously if possible (#2326)
            }, true);
          }
        }

    return this;
  };

  /**
   * Handle an array of source objects
   *
   * @param  {Array} sources Array of source objects
   * @private
   * @method sourceList_
   */

  Player.prototype.sourceList_ = function sourceList_(sources) {
    var sourceTech = this.selectSource(sources);

    if (sourceTech) {
      if (sourceTech.tech === this.techName_) {
        // if this technology is already loaded, set the source
        this.src(sourceTech.source);
      } else {
        // load this technology with the chosen source
        this.loadTech_(sourceTech.tech, sourceTech.source);
      }
    } else {
      // We need to wrap this in a timeout to give folks a chance to add error event handlers
      this.setTimeout(function () {
        this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
      }, 0);

      // we could not find an appropriate tech, but let's still notify the delegate that this is it
      // this needs a better comment about why this is needed
      this.triggerReady();
    }
  };

  /**
   * Begin loading the src data.
   *
   * @return {Player} Returns the player
   * @method load
   */

  Player.prototype.load = function load() {
    this.techCall_('load');
    return this;
  };

  /**
   * Reset the player. Loads the first tech in the techOrder,
   * and calls `reset` on the tech`.
   *
   * @return {Player} Returns the player
   * @method reset
   */

  Player.prototype.reset = function reset() {
    this.loadTech_(_utilsToTitleCaseJs2['default'](this.options_.techOrder[0]), null);
    this.techCall_('reset');
    return this;
  };

  /**
   * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
   * Can be used in conjuction with `currentType` to assist in rebuilding the current source object.
   *
   * @return {String} The current source
   * @method currentSrc
   */

  Player.prototype.currentSrc = function currentSrc() {
    return this.techGet_('currentSrc') || this.cache_.src || '';
  };

  /**
   * Get the current source type e.g. video/mp4
   * This can allow you rebuild the current source object so that you could load the same
   * source and tech later
   *
   * @return {String} The source MIME type
   * @method currentType
   */

  Player.prototype.currentType = function currentType() {
    return this.currentType_ || '';
  };

  /**
   * Get or set the preload attribute
   *
   * @param {Boolean} value Boolean to determine if preload should be used
   * @return {String} The preload attribute value when getting
   * @return {Player} Returns the player when setting
   * @method preload
   */

  Player.prototype.preload = function preload(value) {
    if (value !== undefined) {
      this.techCall_('setPreload', value);
      this.options_.preload = value;
      return this;
    }
    return this.techGet_('preload');
  };

  /**
   * Get or set the autoplay attribute.
   *
   * @param {Boolean} value Boolean to determine if video should autoplay
   * @return {String} The autoplay attribute value when getting
   * @return {Player} Returns the player when setting
   * @method autoplay
   */

  Player.prototype.autoplay = function autoplay(value) {
    if (value !== undefined) {
      this.techCall_('setAutoplay', value);
      this.options_.autoplay = value;
      return this;
    }
    return this.techGet_('autoplay', value);
  };

  /**
   * Set or unset the playsinline attribute.
   * Playsinline tells the browser that non-fullscreen playback is preferred.
   *
   * @param {boolean} [value]
   *        - true means that we should try to play inline by default
   *        - false means that we should use the browser's default playback mode,
   *          which in most cases is inline. iOS Safari is a notable exception
   *          and plays fullscreen by default.
   *
   * @return {string|Player}
   *         - the current value of playsinline
   *         - the player when setting
   *
   * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
   */

  Player.prototype.playsinline = function playsinline(value) {
    if (value !== undefined) {
      this.techCall_('setPlaysinline', value);
      this.options_.playsinline = value;
      return this;
    }
    return this.techGet_('playsinline');
  };

  /**
   * Get or set the loop attribute on the video element.
   *
   * @param {Boolean} value Boolean to determine if video should loop
   * @return {String} The loop attribute value when getting
   * @return {Player} Returns the player when setting
   * @method loop
   */

  Player.prototype.loop = function loop(value) {
    if (value !== undefined) {
      this.techCall_('setLoop', value);
      this.options_['loop'] = value;
      return this;
    }
    return this.techGet_('loop');
  };

  /**
   * Get or set the poster image source url
   *
   * ##### EXAMPLE:
   * ```js
   *     // get
   *     var currentPoster = myPlayer.poster();
   *     // set
   *     myPlayer.poster('http://example.com/myImage.jpg');
   * ```
   *
   * @param  {String=} src Poster image source URL
   * @return {String} poster URL when getting
   * @return {Player} self when setting
   * @method poster
   */

  Player.prototype.poster = function poster(src) {
    if (src === undefined) {
      return this.poster_;
    }

    // The correct way to remove a poster is to set as an empty string
    // other falsey values will throw errors
    if (!src) {
      src = '';
    }

    // update the internal poster variable
    this.poster_ = src;

    // update the tech's poster
    this.techCall_('setPoster', src);

    // alert components that the poster has been set
    this.trigger('posterchange');

    return this;
  };

  /**
   * Some techs (e.g. YouTube) can provide a poster source in an
   * asynchronous way. We want the poster component to use this
   * poster source so that it covers up the tech's controls.
   * (YouTube's play button). However we only want to use this
   * soruce if the player user hasn't set a poster through
   * the normal APIs.
   *
   * @private
   * @method handleTechPosterChange_
   */

  Player.prototype.handleTechPosterChange_ = function handleTechPosterChange_() {
    if (!this.poster_ && this.tech_ && this.tech_.poster) {
      this.poster_ = this.tech_.poster() || '';

      // Let components know the poster has changed
      this.trigger('posterchange');
    }
  };

  /**
   * Get or set whether or not the controls are showing.
   *
   * @param  {Boolean} bool Set controls to showing or not
   * @return {Boolean}    Controls are showing
   * @method controls
   */

  Player.prototype.controls = function controls(bool) {
    if (bool !== undefined) {
      bool = !!bool; // force boolean
      // Don't trigger a change event unless it actually changed
      if (this.controls_ !== bool) {
        this.controls_ = bool;

        if (this.usingNativeControls()) {
          this.techCall_('setControls', bool);
        }

        if (bool) {
          this.removeClass('vjs-controls-disabled');
          this.addClass('vjs-controls-enabled');
          this.trigger('controlsenabled');

          if (!this.usingNativeControls()) {
            this.addTechControlsListeners_();
          }
        } else {
          this.removeClass('vjs-controls-enabled');
          this.addClass('vjs-controls-disabled');
          this.trigger('controlsdisabled');

          if (!this.usingNativeControls()) {
            this.removeTechControlsListeners_();
          }
        }
      }
      return this;
    }
    return !!this.controls_;
  };

  /**
   * Toggle native controls on/off. Native controls are the controls built into
   * devices (e.g. default iPhone controls), Flash, or other techs
   * (e.g. Vimeo Controls)
   * **This should only be set by the current tech, because only the tech knows
   * if it can support native controls**
   *
   * @param  {Boolean} bool    True signals that native controls are on
   * @return {Player}      Returns the player
   * @private
   * @method usingNativeControls
   */

  Player.prototype.usingNativeControls = function usingNativeControls(bool) {
    if (bool !== undefined) {
      bool = !!bool; // force boolean
      // Don't trigger a change event unless it actually changed
      if (this.usingNativeControls_ !== bool) {
        this.usingNativeControls_ = bool;
        if (bool) {
          this.addClass('vjs-using-native-controls');

          /**
            * player is using the native device controls
           *
            * @event usingnativecontrols
            * @memberof Player
            * @instance
            * @private
            */
          this.trigger('usingnativecontrols');
        } else {
          this.removeClass('vjs-using-native-controls');

          /**
            * player is using the custom HTML controls
           *
            * @event usingcustomcontrols
            * @memberof Player
            * @instance
            * @private
            */
          this.trigger('usingcustomcontrols');
        }
      }
      return this;
    }
    return !!this.usingNativeControls_;
  };

  /**
   * Set or get the current MediaError
   *
   * @param  {*} err A MediaError or a String/Number to be turned into a MediaError
   * @return {MediaError|null}     when getting
   * @return {Player}              when setting
   * @method error
   */

  Player.prototype.error = function error(err) {
    if (err === undefined) {
      return this.error_ || null;
    }

    // restoring to default
    if (err === null) {
      this.error_ = err;
      this.removeClass('vjs-error');
      this.errorDisplay.close();
      return this;
    }

    // error instance
    if (err instanceof _mediaErrorJs2['default']) {
      this.error_ = err;
    } else {
      this.error_ = new _mediaErrorJs2['default'](err);
    }

    // add the vjs-error classname to the player
    this.addClass('vjs-error');

    // log the name of the error type and any message
    // ie8 just logs "[object object]" if you just log the error object
    _utilsLogJs2['default'].error('(CODE:' + this.error_.code + ' ' + _mediaErrorJs2['default'].errorTypes[this.error_.code] + ')', this.error_.message, this.error_);

    // fire an error event on the player
    this.trigger('error');

    return this;
  };

  /**
   * Returns whether or not the player is in the "ended" state.
   *
   * @return {Boolean} True if the player is in the ended state, false if not.
   * @method ended
   */

  Player.prototype.ended = function ended() {
    return this.techGet_('ended');
  };

  /**
   * Returns whether or not the player is in the "seeking" state.
   *
   * @return {Boolean} True if the player is in the seeking state, false if not.
   * @method seeking
   */

  Player.prototype.seeking = function seeking() {
    return this.techGet_('seeking');
  };

  /**
   * Returns the TimeRanges of the media that are currently available
   * for seeking to.
   *
   * @return {TimeRanges} the seekable intervals of the media timeline
   * @method seekable
   */

  Player.prototype.seekable = function seekable() {
    return this.techGet_('seekable');
  };

  /**
   * Report user activity
   *
   * @param {Object} event Event object
   * @method reportUserActivity
   */

  Player.prototype.reportUserActivity = function reportUserActivity(event) {
    this.userActivity_ = true;
  };

  /**
   * Get/set if user is active
   *
   * @param {Boolean} bool Value when setting
   * @return {Boolean} Value if user is active user when getting
   * @method userActive
   */

  Player.prototype.userActive = function userActive(bool) {
    if (bool !== undefined) {
      bool = !!bool;
      if (bool !== this.userActive_) {
        this.userActive_ = bool;
        if (bool) {
          // If the user was inactive and is now active we want to reset the
          // inactivity timer
          this.userActivity_ = true;
          this.removeClass('vjs-user-inactive');
          this.addClass('vjs-user-active');
          this.trigger('useractive');
        } else {
          // We're switching the state to inactive manually, so erase any other
          // activity
          this.userActivity_ = false;

          // Chrome/Safari/IE have bugs where when you change the cursor it can
          // trigger a mousemove event. This causes an issue when you're hiding
          // the cursor when the user is inactive, and a mousemove signals user
          // activity. Making it impossible to go into inactive mode. Specifically
          // this happens in fullscreen when we really need to hide the cursor.
          //
          // When this gets resolved in ALL browsers it can be removed
          // https://code.google.com/p/chromium/issues/detail?id=103041
          if (this.tech_) {
            this.tech_.one('mousemove', function (e) {
              e.stopPropagation();
              e.preventDefault();
            });
          }

          this.removeClass('vjs-user-active');
          this.addClass('vjs-user-inactive');
          this.trigger('userinactive');
        }
      }
      return this;
    }
    return this.userActive_;
  };

  /**
   * Listen for user activity based on timeout value
   *
   * @private
   * @method listenForUserActivity_
   */

  Player.prototype.listenForUserActivity_ = function listenForUserActivity_() {
    var mouseInProgress = undefined,
        lastMoveX = undefined,
        lastMoveY = undefined;

    var handleActivity = Fn.bind(this, this.reportUserActivity);

    var handleMouseMove = function handleMouseMove(e) {
      // #1068 - Prevent mousemove spamming
      // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
      if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
        lastMoveX = e.screenX;
        lastMoveY = e.screenY;
        handleActivity();
      }
    };

    var handleMouseDown = function handleMouseDown(e) {
      handleActivity();
      // For as long as the they are touching the device or have their mouse down,
      // we consider them active even if they're not moving their finger or mouse.
      // So we want to continue to update that they are active
      this.clearInterval(mouseInProgress);
      // Setting userActivity=true now and setting the interval to the same time
      // as the activityCheck interval (250) should ensure we never miss the
      // next activityCheck
      if (e.target.nodeName !== 'IFRAME' || e.isTrusted) {
        // do not set interval for events that come from google ima sdk iframe
        // because mouseup may be missing
        mouseInProgress = this.setInterval(handleActivity, 250);
      }
    };

    var handleMouseUp = function handleMouseUp(event) {
      handleActivity();
      // Stop the interval that maintains activity if the mouse/touch is down
      this.clearInterval(mouseInProgress);
    };

    // Any mouse movement will be considered user activity
    this.on('mousedown', handleMouseDown);
    this.on('mousemove', handleMouseMove);
    this.on('mouseup', handleMouseUp);

    // Listen for keyboard navigation
    // Shouldn't need to use inProgress interval because of key repeat
    this.on('keydown', handleActivity);
    this.on('keyup', handleActivity);

    // Run an interval every 250 milliseconds instead of stuffing everything into
    // the mousemove/touchmove function itself, to prevent performance degradation.
    // `this.reportUserActivity` simply sets this.userActivity_ to true, which
    // then gets picked up by this loop
    // http://ejohn.org/blog/learning-from-twitter/
    var inactivityTimeout = undefined;
    var activityCheck = this.setInterval(function () {
      // Check to see if mouse/touch activity has happened
      if (this.userActivity_) {
        // Reset the activity tracker
        this.userActivity_ = false;

        // If the user state was inactive, set the state to active
        this.userActive(true);

        // Clear any existing inactivity timeout to start the timer over
        this.clearTimeout(inactivityTimeout);

        var timeout = this.options_['inactivityTimeout'];
        if (timeout > 0) {
          // In <timeout> milliseconds, if no more activity has occurred the
          // user will be considered inactive
          inactivityTimeout = this.setTimeout(function () {
            // Protect against the case where the inactivityTimeout can trigger just
            // before the next user activity is picked up by the activityCheck loop
            // causing a flicker
            if (!this.userActivity_) {
              this.userActive(false);
            }
          }, timeout);
        }
      }
    }, 250);
  };

  /**
   * Gets or sets the current playback rate.  A playback rate of
   * 1.0 represents normal speed and 0.5 would indicate half-speed
   * playback, for instance.
   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
   *
   * @param  {Number} rate    New playback rate to set.
   * @return {Number}         Returns the new playback rate when setting
   * @return {Number}         Returns the current playback rate when getting
   * @method playbackRate
   */

  Player.prototype.playbackRate = function playbackRate(rate) {
    if (rate !== undefined) {
      this.techCall_('setPlaybackRate', rate);
      return this;
    }

    if (this.tech_ && this.tech_['featuresPlaybackRate']) {
      return this.techGet_('playbackRate');
    } else {
      return 1.0;
    }
  };

  /**
   * Gets or sets the audio flag
   *
   * @param  {Boolean} bool    True signals that this is an audio player.
   * @return {Boolean}         Returns true if player is audio, false if not when getting
   * @return {Player}      Returns the player if setting
   * @private
   * @method isAudio
   */

  Player.prototype.isAudio = function isAudio(bool) {
    if (bool !== undefined) {
      this.isAudio_ = !!bool;
      return this;
    }

    return !!this.isAudio_;
  };

  /**
   * Returns the current state of network activity for the element, from
   * the codes in the list below.
   * - NETWORK_EMPTY (numeric value 0)
   *   The element has not yet been initialised. All attributes are in
   *   their initial states.
   * - NETWORK_IDLE (numeric value 1)
   *   The element's resource selection algorithm is active and has
   *   selected a resource, but it is not actually using the network at
   *   this time.
   * - NETWORK_LOADING (numeric value 2)
   *   The user agent is actively trying to download data.
   * - NETWORK_NO_SOURCE (numeric value 3)
   *   The element's resource selection algorithm is active, but it has
   *   not yet found a resource to use.
   *
   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
   * @return {Number} the current network activity state
   * @method networkState
   */

  Player.prototype.networkState = function networkState() {
    return this.techGet_('networkState');
  };

  /**
   * Returns a value that expresses the current state of the element
   * with respect to rendering the current playback position, from the
   * codes in the list below.
   * - HAVE_NOTHING (numeric value 0)
   *   No information regarding the media resource is available.
   * - HAVE_METADATA (numeric value 1)
   *   Enough of the resource has been obtained that the duration of the
   *   resource is available.
   * - HAVE_CURRENT_DATA (numeric value 2)
   *   Data for the immediate current playback position is available.
   * - HAVE_FUTURE_DATA (numeric value 3)
   *   Data for the immediate current playback position is available, as
   *   well as enough data for the user agent to advance the current
   *   playback position in the direction of playback.
   * - HAVE_ENOUGH_DATA (numeric value 4)
   *   The user agent estimates that enough data is available for
   *   playback to proceed uninterrupted.
   *
   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
   * @return {Number} the current playback rendering state
   * @method readyState
   */

  Player.prototype.readyState = function readyState() {
    return this.techGet_('readyState');
  };

  /**
   * Get a video track list
   * @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
   *
   * @return {VideoTrackList} thes current video track list
   * @method videoTracks
   */

  Player.prototype.videoTracks = function videoTracks() {
    // if we have not yet loadTech_, we create videoTracks_
    // these will be passed to the tech during loading
    if (!this.tech_) {
      this.videoTracks_ = this.videoTracks_ || new _tracksVideoTrackListJs2['default']();
      return this.videoTracks_;
    }

    return this.tech_.videoTracks();
  };

  /**
   * Get an audio track list
   * @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
   *
   * @return {AudioTrackList} thes current audio track list
   * @method audioTracks
   */

  Player.prototype.audioTracks = function audioTracks() {
    // if we have not yet loadTech_, we create videoTracks_
    // these will be passed to the tech during loading
    if (!this.tech_) {
      this.audioTracks_ = this.audioTracks_ || new _tracksAudioTrackListJs2['default']();
      return this.audioTracks_;
    }

    return this.tech_.audioTracks();
  };

  /*
    * Text tracks are tracks of timed text events.
    * Captions - text displayed over the video for the hearing impaired
    * Subtitles - text displayed over the video for those who don't understand language in the video
    * Chapters - text displayed in a menu allowing the user to jump to particular points (chapters) in the video
    * Descriptions (not supported yet) - audio descriptions that are read back to the user by a screen reading device
    */

  /**
   * Get an array of associated text tracks. captions, subtitles, chapters, descriptions
   * http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
   *
   * @return {Array}           Array of track objects
   * @method textTracks
   */

  Player.prototype.textTracks = function textTracks() {
    // cannot use techGet_ directly because it checks to see whether the tech is ready.
    // Flash is unlikely to be ready in time but textTracks should still work.
    return this.tech_ && this.tech_['textTracks']();
  };

  /**
   * Get an array of remote text tracks
   *
   * @return {Array}
   * @method remoteTextTracks
   */

  Player.prototype.remoteTextTracks = function remoteTextTracks() {
    return this.tech_ && this.tech_['remoteTextTracks']();
  };

  /**
   * Get an array of remote html track elements
   *
   * @return {HTMLTrackElement[]}
   * @method remoteTextTrackEls
   */

  Player.prototype.remoteTextTrackEls = function remoteTextTrackEls() {
    return this.tech_ && this.tech_['remoteTextTrackEls']();
  };

  /**
   * Add a text track
   * In addition to the W3C settings we allow adding additional info through options.
   * http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
   *
   * @param {String}  kind        Captions, subtitles, chapters, descriptions, or metadata
   * @param {String=} label       Optional label
   * @param {String=} language    Optional language
   * @method addTextTrack
   */

  Player.prototype.addTextTrack = function addTextTrack(kind, label, language) {
    return this.tech_ && this.tech_['addTextTrack'](kind, label, language);
  };

  /**
   * Add a remote text track
   *
   * @param {Object} options    Options for remote text track
   * @method addRemoteTextTrack
   */

  Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options) {
    return this.tech_ && this.tech_['addRemoteTextTrack'](options);
  };

  /**
   * Remove a remote text track
   *
   * @param {Object} track    Remote text track to remove
   * @method removeRemoteTextTrack
   */
  // destructure the input into an object with a track argument, defaulting to arguments[0]
  // default the whole argument to an empty object if nothing was passed in

  Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() {
    var _ref3 = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

    var _ref3$track = _ref3.track;
    var track = _ref3$track === undefined ? arguments[0] : _ref3$track;
    // jshint ignore:line
    this.tech_ && this.tech_['removeRemoteTextTrack'](track);
  };

  /**
   * Get video width
   *
   * @return {Number} Video width
   * @method videoWidth
   */

  Player.prototype.videoWidth = function videoWidth() {
    return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
  };

  /**
   * Get video height
   *
   * @return {Number} Video height
   * @method videoHeight
   */

  Player.prototype.videoHeight = function videoHeight() {
    return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
  };

  // Methods to add support for
  // initialTime: function(){ return this.techCall_('initialTime'); },
  // startOffsetTime: function(){ return this.techCall_('startOffsetTime'); },
  // played: function(){ return this.techCall_('played'); },
  // defaultPlaybackRate: function(){ return this.techCall_('defaultPlaybackRate'); },
  // defaultMuted: function(){ return this.techCall_('defaultMuted'); }

  /**
   * The player's language code
   * NOTE: The language should be set in the player options if you want the
   * the controls to be built with a specific language. Changing the lanugage
   * later will not update controls text.
   *
   * @param {String} code  The locale string
   * @return {String}      The locale string when getting
   * @return {Player}      self when setting
   * @method language
   */

  Player.prototype.language = function language(code) {
    if (code === undefined) {
      return this.language_;
    }

    this.language_ = ('' + code).toLowerCase();
    return this;
  };

  /**
   * Get the player's language dictionary
   * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
   * Languages specified directly in the player options have precedence
   *
   * @return {Array} Array of languages
   * @method languages
   */

  Player.prototype.languages = function languages() {
    return _utilsMergeOptionsJs2['default'](Player.prototype.options_.languages, this.languages_);
  };

  /**
   * Converts track info to JSON
   *
   * @return {Object} JSON object of options
   * @method toJSON
   */

  Player.prototype.toJSON = function toJSON() {
    var options = _utilsMergeOptionsJs2['default'](this.options_);
    var tracks = options.tracks;

    options.tracks = [];

    for (var i = 0; i < tracks.length; i++) {
      var track = tracks[i];

      // deep merge tracks and null out player so no circular references
      track = _utilsMergeOptionsJs2['default'](track);
      track.player = undefined;
      options.tracks[i] = track;
    }

    return options;
  };

  /**
   * Creates a simple modal dialog (an instance of the `ModalDialog`
   * component) that immediately overlays the player with arbitrary
   * content and removes itself when closed.
   *
   * @param {String|Function|Element|Array|Null} content
   *        Same as `ModalDialog#content`'s param of the same name.
   *
   *        The most straight-forward usage is to provide a string or DOM
   *        element.
   *
   * @param {Object} [options]
   *        Extra options which will be passed on to the `ModalDialog`.
   *
   * @return {ModalDialog}
   */

  Player.prototype.createModal = function createModal(content, options) {
    var player = this;

    options = options || {};
    options.content = content || '';

    var modal = new _modalDialog2['default'](player, options);

    player.addChild(modal);
    modal.on('dispose', function () {
      player.removeChild(modal);
    });

    return modal.open();
  };

  /**
   * Gets tag settings
   *
   * @param {Element} tag The player tag
   * @return {Array} An array of sources and track objects
   * @static
   * @method getTagSettings
   */

  Player.getTagSettings = function getTagSettings(tag) {
    var baseOptions = {
      'sources': [],
      'tracks': []
    };

    var tagOptions = Dom.getElAttributes(tag);
    var dataSetup = tagOptions['data-setup'];

    // Check if data-setup attr exists.
    if (dataSetup !== null) {
      // Parse options JSON

      var _safeParseTuple = _safeJsonParseTuple2['default'](dataSetup || '{}');

      var err = _safeParseTuple[0];
      var data = _safeParseTuple[1];

      if (err) {
        _utilsLogJs2['default'].error(err);
      }
      _objectAssign2['default'](tagOptions, data);
    }

    _objectAssign2['default'](baseOptions, tagOptions);

    // Get tag children settings
    if (tag.hasChildNodes()) {
      var children = tag.childNodes;

      for (var i = 0, j = children.length; i < j; i++) {
        var child = children[i];
        // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
        var childName = child.nodeName.toLowerCase();
        if (childName === 'source') {
          baseOptions.sources.push(Dom.getElAttributes(child));
        } else if (childName === 'track') {
          baseOptions.tracks.push(Dom.getElAttributes(child));
        }
      }
    }

    return baseOptions;
  };

  return Player;
})(_componentJs2['default']);

Player.players = {};

var navigator = _globalWindow2['default'].navigator;
/*
 * Player instance options, surfaced using options
 * options = Player.prototype.options_
 * Make changes in options, not here.
 *
 * @type {Object}
 * @private
 */
Player.prototype.options_ = {
  // Default order of fallback technology
  techOrder: ['html5', 'flash'],
  // techOrder: ['flash','html5'],

  html5: {},
  flash: {},

  // defaultVolume: 0.85,
  defaultVolume: 0.00, // The freakin seaguls are driving me crazy!

  // default inactivity timeout
  inactivityTimeout: 2000,

  // default playback rates
  playbackRates: [],
  // Add playback rate selection by adding rates
  // 'playbackRates': [0.5, 1, 1.5, 2],

  // Included control sets
  children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'controlBar', 'errorDisplay', 'textTrackSettings'],

  language: _globalDocument2['default'].getElementsByTagName('html')[0].getAttribute('lang') || navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language || 'en',

  // locales and their language translations
  languages: {},

  // Default message to show when a video cannot be played.
  notSupportedMessage: 'No compatible source was found for this media.'
};

/**
 * Fired when the player has initial duration and dimension information
 *
 * @event loadedmetadata
 */
Player.prototype.handleLoadedMetaData_;

/**
 * Fired when the player has downloaded data at the current playback position
 *
 * @event loadeddata
 */
Player.prototype.handleLoadedData_;

/**
 * Fired when the user is active, e.g. moves the mouse over the player
 *
 * @event useractive
 */
Player.prototype.handleUserActive_;

/**
 * Fired when the user is inactive, e.g. a short delay after the last mouse move or control interaction
 *
 * @event userinactive
 */
Player.prototype.handleUserInactive_;

/**
 * Fired when the current playback position has changed *
 * During playback this is fired every 15-250 milliseconds, depending on the
 * playback technology in use.
 *
 * @event timeupdate
 */
Player.prototype.handleTimeUpdate_;

/**
 * Fired when video playback ends
 *
 * @event ended
 */
Player.prototype.handleTechEnded_;

/**
 * Fired when the volume changes
 *
 * @event volumechange
 */
Player.prototype.handleVolumeChange_;

/**
 * Fired when an error occurs
 *
 * @event error
 */
Player.prototype.handleError_;

Player.prototype.flexNotSupported_ = function () {
  var elem = _globalDocument2['default'].createElement('i');

  // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
  // common flex features that we can rely on when checking for flex support.
  return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style || 'msFlexOrder' in elem.style) /* IE10-specific (2012 flex spec)  */;
};

_componentJs2['default'].registerComponent('Player', Player);
exports['default'] = Player;
module.exports = exports['default'];
// If empty string, make it a parsable json object.

},{"./big-play-button.js":62,"./component.js":66,"./control-bar/control-bar.js":69,"./error-display.js":103,"./fullscreen-api.js":106,"./loading-spinner.js":107,"./media-error.js":108,"./modal-dialog":112,"./poster-image.js":117,"./tech/html5.js":122,"./tech/loader.js":123,"./tech/tech.js":124,"./tracks/audio-track-list.js":126,"./tracks/text-track-display.js":131,"./tracks/text-track-list-converter.js":132,"./tracks/text-track-settings.js":134,"./tracks/video-track-list.js":139,"./utils/browser.js":141,"./utils/buffer.js":142,"./utils/dom.js":144,"./utils/events.js":145,"./utils/fn.js":146,"./utils/guid.js":148,"./utils/log.js":149,"./utils/merge-options.js":150,"./utils/stylesheet.js":151,"./utils/time-ranges.js":152,"./utils/to-title-case.js":153,"global/document":7,"global/window":8,"object.assign":53,"safe-json-parse/tuple":57}],114:[function(_dereq_,module,exports){
/**
 * @file plugins.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _playerJs = _dereq_('./player.js');

var _playerJs2 = _interopRequireDefault(_playerJs);

/**
 * The method for registering a video.js plugin
 *
 * @param  {String} name The name of the plugin
 * @param  {Function} init The function that is run when the player inits
 * @method plugin
 */
var plugin = function plugin(name, init) {
  _playerJs2['default'].prototype[name] = init;
};

exports['default'] = plugin;
module.exports = exports['default'];

},{"./player.js":113}],115:[function(_dereq_,module,exports){
/**
 * @file popup-button.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _clickableComponentJs = _dereq_('../clickable-component.js');

var _clickableComponentJs2 = _interopRequireDefault(_clickableComponentJs);

var _componentJs = _dereq_('../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _popupJs = _dereq_('./popup.js');

var _popupJs2 = _interopRequireDefault(_popupJs);

var _utilsDomJs = _dereq_('../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsToTitleCaseJs = _dereq_('../utils/to-title-case.js');

var _utilsToTitleCaseJs2 = _interopRequireDefault(_utilsToTitleCaseJs);

/**
 * A button class with a popup control
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends ClickableComponent
 * @class PopupButton
 */

var PopupButton = (function (_ClickableComponent) {
  _inherits(PopupButton, _ClickableComponent);

  function PopupButton(player) {
    var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

    _classCallCheck(this, PopupButton);

    _ClickableComponent.call(this, player, options);

    this.update();
  }

  /**
   * Update popup
   *
   * @method update
   */

  PopupButton.prototype.update = function update() {
    var popup = this.createPopup();

    if (this.popup) {
      this.removeChild(this.popup);
    }

    this.popup = popup;
    this.addChild(popup);

    if (this.items && this.items.length === 0) {
      this.hide();
    } else if (this.items && this.items.length > 1) {
      this.show();
    }
  };

  /**
   * Create popup - Override with specific functionality for component
   *
   * @return {Popup} The constructed popup
   * @method createPopup
   */

  PopupButton.prototype.createPopup = function createPopup() {};

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  PopupButton.prototype.createEl = function createEl() {
    return _ClickableComponent.prototype.createEl.call(this, 'div', {
      className: this.buildCSSClass()
    });
  };

  /**
   * Allow sub components to stack CSS class names
   *
   * @return {String} The constructed class name
   * @method buildCSSClass
   */

  PopupButton.prototype.buildCSSClass = function buildCSSClass() {
    var menuButtonClass = 'vjs-menu-button';

    // If the inline option is passed, we want to use different styles altogether.
    if (this.options_.inline === true) {
      menuButtonClass += '-inline';
    } else {
      menuButtonClass += '-popup';
    }

    return 'vjs-menu-button ' + menuButtonClass + ' ' + _ClickableComponent.prototype.buildCSSClass.call(this);
  };

  /**
   * Return handler for the tooltip
   *
   * @return {Object} Dom element to serve as a handler for the tooltip
   * @method tooltipHandler
   */

  PopupButton.prototype.tooltipHandler = function tooltipHandler() {
    // TODO: Fix popup button to have a real icon element to make it as tooltip handler
    return;
  };

  return PopupButton;
})(_clickableComponentJs2['default']);

_componentJs2['default'].registerComponent('PopupButton', PopupButton);
exports['default'] = PopupButton;
module.exports = exports['default'];

},{"../clickable-component.js":64,"../component.js":66,"../utils/dom.js":144,"../utils/fn.js":146,"../utils/to-title-case.js":153,"./popup.js":116}],116:[function(_dereq_,module,exports){
/**
 * @file popup.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsDomJs = _dereq_('../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsEventsJs = _dereq_('../utils/events.js');

var Events = _interopRequireWildcard(_utilsEventsJs);

/**
 * The Popup component is used to build pop up controls.
 *
 * @extends Component
 * @class Popup
 */

var Popup = (function (_Component) {
  _inherits(Popup, _Component);

  function Popup() {
    _classCallCheck(this, Popup);

    _Component.apply(this, arguments);
  }

  /**
   * Add a popup item to the popup
   *
   * @param {Object|String} component Component or component type to add
   * @method addItem
   */

  Popup.prototype.addItem = function addItem(component) {
    this.addChild(component);
    component.on('click', Fn.bind(this, function () {
      this.unlockShowing();
    }));
  };

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  Popup.prototype.createEl = function createEl() {
    var contentElType = this.options_.contentElType || 'ul';
    this.contentEl_ = Dom.createEl(contentElType, {
      className: 'vjs-menu-content'
    });
    var el = _Component.prototype.createEl.call(this, 'div', {
      append: this.contentEl_,
      className: 'vjs-menu'
    });
    el.appendChild(this.contentEl_);

    // Prevent clicks from bubbling up. Needed for Popup Buttons,
    // where a click on the parent is significant
    Events.on(el, 'click', function (event) {
      event.preventDefault();
      event.stopImmediatePropagation();
    });

    return el;
  };

  return Popup;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('Popup', Popup);
exports['default'] = Popup;
module.exports = exports['default'];

},{"../component.js":66,"../utils/dom.js":144,"../utils/events.js":145,"../utils/fn.js":146}],117:[function(_dereq_,module,exports){
/**
 * @file poster-image.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _clickableComponentJs = _dereq_('./clickable-component.js');

var _clickableComponentJs2 = _interopRequireDefault(_clickableComponentJs);

var _componentJs = _dereq_('./component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsFnJs = _dereq_('./utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsDomJs = _dereq_('./utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsBrowserJs = _dereq_('./utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

/**
 * The component that handles showing the poster image.
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Button
 * @class PosterImage
 */

var PosterImage = (function (_ClickableComponent) {
  _inherits(PosterImage, _ClickableComponent);

  function PosterImage(player, options) {
    _classCallCheck(this, PosterImage);

    _ClickableComponent.call(this, player, options);

    this.update();
    player.on('posterchange', Fn.bind(this, this.update));
  }

  /**
   * Clean up the poster image
   *
   * @method dispose
   */

  PosterImage.prototype.dispose = function dispose() {
    this.player().off('posterchange', this.update);
    _ClickableComponent.prototype.dispose.call(this);
  };

  /**
   * Create the poster's image element
   *
   * @return {Element}
   * @method createEl
   */

  PosterImage.prototype.createEl = function createEl() {
    var el = Dom.createEl('div', {
      className: 'vjs-poster',

      // Don't want poster to be tabbable.
      tabIndex: -1
    });

    // To ensure the poster image resizes while maintaining its original aspect
    // ratio, use a div with `background-size` when available. For browsers that
    // do not support `background-size` (e.g. IE8), fall back on using a regular
    // img element.
    if (!browser.BACKGROUND_SIZE_SUPPORTED) {
      this.fallbackImg_ = Dom.createEl('img');
      el.appendChild(this.fallbackImg_);
    }

    return el;
  };

  /**
   * Event handler for updates to the player's poster source
   *
   * @method update
   */

  PosterImage.prototype.update = function update() {
    var url = this.player().poster();

    this.setSrc(url);

    // If there's no poster source we should display:none on this component
    // so it's not still clickable or right-clickable
    if (url) {
      this.show();
    } else {
      this.hide();
    }
  };

  /**
   * Set the poster source depending on the display method
   *
   * @param {String} url The URL to the poster source
   * @method setSrc
   */

  PosterImage.prototype.setSrc = function setSrc(url) {
    if (this.fallbackImg_) {
      this.fallbackImg_.src = url;
    } else {
      var backgroundImage = '';
      // Any falsey values should stay as an empty string, otherwise
      // this will throw an extra error
      if (url) {
        backgroundImage = 'url("' + url + '")';
      }

      this.el_.style.backgroundImage = backgroundImage;
    }
  };

  /**
   * Event handler for clicks on the poster image
   *
   * @method handleClick
   */

  PosterImage.prototype.handleClick = function handleClick() {
    // We don't want a click to trigger playback when controls are disabled
    // but CSS should be hiding the poster to prevent that from happening
    if (this.player_.paused()) {
      this.player_.play();
    } else {
      this.player_.pause();
    }
  };

  return PosterImage;
})(_clickableComponentJs2['default']);

_componentJs2['default'].registerComponent('PosterImage', PosterImage);
exports['default'] = PosterImage;
module.exports = exports['default'];

},{"./clickable-component.js":64,"./component.js":66,"./utils/browser.js":141,"./utils/dom.js":144,"./utils/fn.js":146}],118:[function(_dereq_,module,exports){
/**
 * @file setup.js
 *
 * Functions for automatically setting up a player
 * based on the data-setup attribute of the video tag
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _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; } }

var _utilsEventsJs = _dereq_('./utils/events.js');

var Events = _interopRequireWildcard(_utilsEventsJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _windowLoaded = false;
var videojs = undefined;

// Automatically set up any tags that have a data-setup attribute
var autoSetup = function autoSetup() {
  // One day, when we stop supporting IE8, go back to this, but in the meantime...*hack hack hack*
  // var vids = Array.prototype.slice.call(document.getElementsByTagName('video'));
  // var audios = Array.prototype.slice.call(document.getElementsByTagName('audio'));
  // var mediaEls = vids.concat(audios);

  // Because IE8 doesn't support calling slice on a node list, we need to loop through each list of elements
  // to build up a new, combined list of elements.
  var vids = _globalDocument2['default'].getElementsByTagName('video');
  var audios = _globalDocument2['default'].getElementsByTagName('audio');
  var mediaEls = [];
  if (vids && vids.length > 0) {
    for (var i = 0, e = vids.length; i < e; i++) {
      mediaEls.push(vids[i]);
    }
  }
  if (audios && audios.length > 0) {
    for (var i = 0, e = audios.length; i < e; i++) {
      mediaEls.push(audios[i]);
    }
  }

  // Check if any media elements exist
  if (mediaEls && mediaEls.length > 0) {

    for (var i = 0, e = mediaEls.length; i < e; i++) {
      var mediaEl = mediaEls[i];

      // Check if element exists, has getAttribute func.
      // IE seems to consider typeof el.getAttribute == 'object' instead of 'function' like expected, at least when loading the player immediately.
      if (mediaEl && mediaEl.getAttribute) {

        // Make sure this player hasn't already been set up.
        if (mediaEl['player'] === undefined) {
          var options = mediaEl.getAttribute('data-setup');

          // Check if data-setup attr exists.
          // We only auto-setup if they've added the data-setup attr.
          if (options !== null) {
            // Create new video.js instance.
            var player = videojs(mediaEl);
          }
        }

        // If getAttribute isn't defined, we need to wait for the DOM.
      } else {
          autoSetupTimeout(1);
          break;
        }
    }

    // No videos were found, so keep looping unless page is finished loading.
  } else if (!_windowLoaded) {
      autoSetupTimeout(1);
    }
};

// Pause to let the DOM keep processing
var autoSetupTimeout = function autoSetupTimeout(wait, vjs) {
  if (vjs) {
    videojs = vjs;
  }

  setTimeout(autoSetup, wait);
};

if (_globalDocument2['default'].readyState === 'complete') {
  _windowLoaded = true;
} else {
  Events.one(_globalWindow2['default'], 'load', function () {
    _windowLoaded = true;
  });
}

var hasLoaded = function hasLoaded() {
  return _windowLoaded;
};

exports.autoSetup = autoSetup;
exports.autoSetupTimeout = autoSetupTimeout;
exports.hasLoaded = hasLoaded;

},{"./utils/events.js":145,"global/document":7,"global/window":8}],119:[function(_dereq_,module,exports){
/**
 * @file slider.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _utilsDomJs = _dereq_('../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _objectAssign = _dereq_('object.assign');

var _objectAssign2 = _interopRequireDefault(_objectAssign);

/**
 * The base functionality for sliders like the volume bar and seek bar
 *
 * @param {Player|Object} player
 * @param {Object=} options
 * @extends Component
 * @class Slider
 */

var Slider = (function (_Component) {
  _inherits(Slider, _Component);

  function Slider(player, options) {
    _classCallCheck(this, Slider);

    _Component.call(this, player, options);

    // Set property names to bar to match with the child Slider class is looking for
    this.bar = this.getChild(this.options_.barName);

    // Set a horizontal or vertical class on the slider depending on the slider type
    this.vertical(!!this.options_.vertical);

    this.on('mousedown', this.handleMouseDown);
    this.on('touchstart', this.handleMouseDown);
    this.on('focus', this.handleFocus);
    this.on('blur', this.handleBlur);
    this.on('click', this.handleClick);

    this.on(player, 'controlsvisible', this.update);
    this.on(player, this.playerEvent, this.update);
  }

  /**
   * Create the component's DOM element
   *
   * @param {String} type Type of element to create
   * @param {Object=} props List of properties in Object form
   * @return {Element}
   * @method createEl
   */

  Slider.prototype.createEl = function createEl(type) {
    var props = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
    var attributes = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];

    // Add the slider element class to all sub classes
    props.className = props.className + ' vjs-slider';
    props = _objectAssign2['default']({
      tabIndex: 0
    }, props);

    attributes = _objectAssign2['default']({
      'role': 'slider',
      'aria-valuenow': 0,
      'aria-valuemin': 0,
      'aria-valuemax': 100,
      tabIndex: 0
    }, attributes);

    return _Component.prototype.createEl.call(this, type, props, attributes);
  };

  /**
   * Handle mouse down on slider
   *
   * @param {Object} event Mouse down event object
   * @method handleMouseDown
   */

  Slider.prototype.handleMouseDown = function handleMouseDown(event) {
    var doc = this.bar.el_.ownerDocument;

    event.preventDefault();
    Dom.blockTextSelection();

    this.addClass('vjs-sliding');
    this.trigger('slideractive');

    this.on(doc, 'mousemove', this.handleMouseMove);
    this.on(doc, 'mouseup', this.handleMouseUp);
    this.on(doc, 'touchmove', this.handleMouseMove);
    this.on(doc, 'touchend', this.handleMouseUp);

    this.handleMouseMove(event);
  };

  /**
   * To be overridden by a subclass
   *
   * @method handleMouseMove
   */

  Slider.prototype.handleMouseMove = function handleMouseMove() {};

  /**
   * Handle mouse up on Slider
   *
   * @method handleMouseUp
   */

  Slider.prototype.handleMouseUp = function handleMouseUp() {
    var doc = this.bar.el_.ownerDocument;

    Dom.unblockTextSelection();

    this.removeClass('vjs-sliding');
    this.trigger('sliderinactive');

    this.off(doc, 'mousemove', this.handleMouseMove);
    this.off(doc, 'mouseup', this.handleMouseUp);
    this.off(doc, 'touchmove', this.handleMouseMove);
    this.off(doc, 'touchend', this.handleMouseUp);

    this.update();
  };

  /**
   * Update slider
   *
   * @method update
   */

  Slider.prototype.update = function update() {
    // In VolumeBar init we have a setTimeout for update that pops and update to the end of the
    // execution stack. The player is destroyed before then update will cause an error
    if (!this.el_) return;

    // If scrubbing, we could use a cached value to make the handle keep up with the user's mouse.
    // On HTML5 browsers scrubbing is really smooth, but some flash players are slow, so we might want to utilize this later.
    // var progress =  (this.player_.scrubbing()) ? this.player_.getCache().currentTime / this.player_.duration() : this.player_.currentTime() / this.player_.duration();
    var progress = this.getPercent();
    var bar = this.bar;

    // If there's no bar...
    if (!bar) return;

    // Protect against no duration and other division issues
    if (typeof progress !== 'number' || progress !== progress || progress < 0 || progress === Infinity) {
      progress = 0;
    }

    // Convert to a percentage for setting
    var percentage = (progress * 100).toFixed(2) + '%';

    // Set the new bar width or height
    if (this.vertical()) {
      bar.el().style.height = percentage;
    } else {
      bar.el().style.width = percentage;
    }
  };

  /**
   * Calculate distance for slider
   *
   * @param {Object} event Event object
   * @method calculateDistance
   */

  Slider.prototype.calculateDistance = function calculateDistance(event) {
    var position = Dom.getPointerPosition(this.el_, event);
    if (this.vertical()) {
      return position.y;
    }
    return position.x;
  };

  /**
   * Handle on focus for slider
   *
   * @method handleFocus
   */

  Slider.prototype.handleFocus = function handleFocus() {
    this.on(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
  };

  /**
   * Handle key press for slider
   *
   * @param {Object} event Event object
   * @method handleKeyPress
   */

  Slider.prototype.handleKeyPress = function handleKeyPress(event) {
    if (event.which === 37 || event.which === 40) {
      // Left and Down Arrows
      event.preventDefault();
      this.stepBack();
    } else if (event.which === 38 || event.which === 39) {
      // Up and Right Arrows
      event.preventDefault();
      this.stepForward();
    }
  };

  /**
   * Handle on blur for slider
   *
   * @method handleBlur
   */

  Slider.prototype.handleBlur = function handleBlur() {
    this.off(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
  };

  /**
   * Listener for click events on slider, used to prevent clicks
   *   from bubbling up to parent elements like button menus.
   *
   * @param {Object} event Event object
   * @method handleClick
   */

  Slider.prototype.handleClick = function handleClick(event) {
    event.stopImmediatePropagation();
    event.preventDefault();
  };

  /**
   * Get/set if slider is horizontal for vertical
   *
   * @param {Boolean} bool True if slider is vertical, false is horizontal
   * @return {Boolean} True if slider is vertical, false is horizontal
   * @method vertical
   */

  Slider.prototype.vertical = function vertical(bool) {
    if (bool === undefined) {
      return this.vertical_ || false;
    }

    this.vertical_ = !!bool;

    if (this.vertical_) {
      this.addClass('vjs-slider-vertical');
    } else {
      this.addClass('vjs-slider-horizontal');
    }

    return this;
  };

  return Slider;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('Slider', Slider);
exports['default'] = Slider;
module.exports = exports['default'];

},{"../component.js":66,"../utils/dom.js":144,"object.assign":53}],120:[function(_dereq_,module,exports){
/**
 * @file flash-rtmp.js
 */
'use strict';

exports.__esModule = true;
function FlashRtmpDecorator(Flash) {
  Flash.streamingFormats = {
    'rtmp/mp4': 'MP4',
    'rtmp/flv': 'FLV'
  };

  Flash.streamFromParts = function (connection, stream) {
    return connection + '&' + stream;
  };

  Flash.streamToParts = function (src) {
    var parts = {
      connection: '',
      stream: ''
    };

    if (!src) return parts;

    // Look for the normal URL separator we expect, '&'.
    // If found, we split the URL into two pieces around the
    // first '&'.
    var connEnd = src.search(/&(?!\w+=)/);
    var streamBegin = undefined;
    if (connEnd !== -1) {
      streamBegin = connEnd + 1;
    } else {
      // If there's not a '&', we use the last '/' as the delimiter.
      connEnd = streamBegin = src.lastIndexOf('/') + 1;
      if (connEnd === 0) {
        // really, there's not a '/'?
        connEnd = streamBegin = src.length;
      }
    }
    parts.connection = src.substring(0, connEnd);
    parts.stream = src.substring(streamBegin, src.length);

    return parts;
  };

  Flash.isStreamingType = function (srcType) {
    return srcType in Flash.streamingFormats;
  };

  // RTMP has four variations, any string starting
  // with one of these protocols should be valid
  Flash.RTMP_RE = /^rtmp[set]?:\/\//i;

  Flash.isStreamingSrc = function (src) {
    return Flash.RTMP_RE.test(src);
  };

  /**
   * A source handler for RTMP urls
   * @type {Object}
   */
  Flash.rtmpSourceHandler = {};

  /**
   * Check if Flash can play the given videotype
   * @param  {String} type    The mimetype to check
   * @return {String}         'probably', 'maybe', or '' (empty string)
   */
  Flash.rtmpSourceHandler.canPlayType = function (type) {
    if (Flash.isStreamingType(type)) {
      return 'maybe';
    }

    return '';
  };

  /**
   * Check if Flash can handle the source natively
   * @param  {Object} source  The source object
   * @return {String}         'probably', 'maybe', or '' (empty string)
   */
  Flash.rtmpSourceHandler.canHandleSource = function (source) {
    var can = Flash.rtmpSourceHandler.canPlayType(source.type);

    if (can) {
      return can;
    }

    if (Flash.isStreamingSrc(source.src)) {
      return 'maybe';
    }

    return '';
  };

  /**
   * Pass the source to the flash object
   * Adaptive source handlers will have more complicated workflows before passing
   * video data to the video element
   * @param  {Object} source   The source object
   * @param  {Flash}  tech     The instance of the Flash tech
   * @param  {Object} options  The options to pass to the source
   */
  Flash.rtmpSourceHandler.handleSource = function (source, tech, options) {
    var srcParts = Flash.streamToParts(source.src);

    tech['setRtmpConnection'](srcParts.connection);
    tech['setRtmpStream'](srcParts.stream);
  };

  // Register the native source handler
  Flash.registerSourceHandler(Flash.rtmpSourceHandler);

  return Flash;
}

exports['default'] = FlashRtmpDecorator;
module.exports = exports['default'];

},{}],121:[function(_dereq_,module,exports){
/**
 * @file flash.js
 * VideoJS-SWF - Custom Flash Player with HTML5-ish API
 * https://github.com/zencoder/video-js-swf
 * Not using setupTriggers. Using global onEvent func to distribute events
 */

'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _tech = _dereq_('./tech');

var _tech2 = _interopRequireDefault(_tech);

var _utilsDomJs = _dereq_('../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsUrlJs = _dereq_('../utils/url.js');

var Url = _interopRequireWildcard(_utilsUrlJs);

var _utilsTimeRangesJs = _dereq_('../utils/time-ranges.js');

var _flashRtmp = _dereq_('./flash-rtmp');

var _flashRtmp2 = _interopRequireDefault(_flashRtmp);

var _component = _dereq_('../component');

var _component2 = _interopRequireDefault(_component);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _objectAssign = _dereq_('object.assign');

var _objectAssign2 = _interopRequireDefault(_objectAssign);

var navigator = _globalWindow2['default'].navigator;
/**
 * Flash Media Controller - Wrapper for fallback SWF API
 *
 * @param {Object=} options Object of option names and values
 * @param {Function=} ready Ready callback function
 * @extends Tech
 * @class Flash
 */

var Flash = (function (_Tech) {
  _inherits(Flash, _Tech);

  function Flash(options, ready) {
    _classCallCheck(this, Flash);

    _Tech.call(this, options, ready);

    // Set the source when ready
    if (options.source) {
      this.ready(function () {
        this.setSource(options.source);
      }, true);
    }

    // Having issues with Flash reloading on certain page actions (hide/resize/fullscreen) in certain browsers
    // This allows resetting the playhead when we catch the reload
    if (options.startTime) {
      this.ready(function () {
        this.load();
        this.play();
        this.currentTime(options.startTime);
      }, true);
    }

    // Add global window functions that the swf expects
    // A 4.x workflow we weren't able to solve for in 5.0
    // because of the need to hard code these functions
    // into the swf for security reasons
    _globalWindow2['default'].videojs = _globalWindow2['default'].videojs || {};
    _globalWindow2['default'].videojs.Flash = _globalWindow2['default'].videojs.Flash || {};
    _globalWindow2['default'].videojs.Flash.onReady = Flash.onReady;
    _globalWindow2['default'].videojs.Flash.onEvent = Flash.onEvent;
    _globalWindow2['default'].videojs.Flash.onError = Flash.onError;

    this.on('seeked', function () {
      this.lastSeekTarget_ = undefined;
    });
  }

  // Create setters and getters for attributes

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  Flash.prototype.createEl = function createEl() {
    var options = this.options_;

    // If video.js is hosted locally you should also set the location
    // for the hosted swf, which should be relative to the page (not video.js)
    // Otherwise this adds a CDN url.
    // The CDN also auto-adds a swf URL for that specific version.
    if (!options.swf) {
      options.swf = '//cdn.rawgit.com/hola/video-js-swf-sv/v5.1.0-4/dist/video-js.swf';
    }

    // Generate ID for swf object
    var objId = options.techId;

    if (options.accelerated) {
      // setup params needed for hardware acceleration
      options.params = _objectAssign2['default'](options.params || {}, {
        allowfullscreen: true,
        wmode: 'direct'
      });
    }

    // Merge default flashvars with ones passed in to init
    var flashVars = _objectAssign2['default']({

      // SWF Callback Functions
      'readyFunction': 'videojs.Flash.onReady',
      'eventProxyFunction': 'videojs.Flash.onEvent',
      'errorEventProxyFunction': 'videojs.Flash.onError',

      // Player Settings
      'accelerated': options.accelerated,
      'autoplay': options.autoplay,
      'controls': options.controls,
      'poster': options.poster,
      'preload': options.preload,
      'loop': options.loop,
      'muted': options.muted

    }, options.flashVars);

    // Merge default parames with ones passed in
    var params = _objectAssign2['default']({
      'allowfullscreen': true,
      'wmode': 'opaque', // Opaque is needed to overlay controls, but can affect playback performance
      'bgcolor': '#000000' // Using bgcolor prevents a white flash when the object is loading
    }, options.params);

    // Merge default attributes with ones passed in
    var attributes = _objectAssign2['default']({
      'id': objId,
      'name': objId, // Both ID and Name needed or swf to identify itself
      'class': 'vjs-tech'
    }, options.attributes);

    this.el_ = Flash.embed(options.swf, flashVars, params, attributes);
    this.el_.tech = this;

    return this.el_;
  };

  /**
   * Play for flash tech
   *
   * @method play
   */

  Flash.prototype.play = function play() {
    if (this.ended()) {
      this.setCurrentTime(0);
    }
    this.el_.vjs_play();
  };

  /**
   * Pause for flash tech
   *
   * @method pause
   */

  Flash.prototype.pause = function pause() {
    this.el_.vjs_pause();
  };

  /**
   * Get/set video
   *
   * @param {Object=} src Source object
   * @return {Object}
   * @method src
   */

  Flash.prototype.src = function src(_src) {
    if (_src === undefined) {
      return this.currentSrc();
    }

    // Setting src through `src` not `setSrc` will be deprecated
    return this.setSrc(_src);
  };

  /**
   * Set video
   *
   * @param {Object=} src Source object
   * @deprecated
   * @method setSrc
   */

  Flash.prototype.setSrc = function setSrc(src) {
    // Make sure source URL is absolute.
    src = Url.getAbsoluteURL(src);
    this.el_.vjs_src(src);

    // Currently the SWF doesn't autoplay if you load a source later.
    // e.g. Load player w/ no source, wait 2s, set src.
    if (this.autoplay()) {
      var tech = this;
      this.setTimeout(function () {
        tech.play();
      }, 0);
    }
  };

  /**
   * Returns true if the tech is currently seeking.
   * @return {boolean} true if seeking
   */

  Flash.prototype.seeking = function seeking() {
    return this.lastSeekTarget_ !== undefined;
  };

  /**
   * Set current time
   *
   * @param {Number} time Current time of video
   * @method setCurrentTime
   */

  Flash.prototype.setCurrentTime = function setCurrentTime(time) {
    var seekable = this.seekable();
    if (seekable.length) {
      // clamp to the current seekable range
      time = time > seekable.start(0) ? time : seekable.start(0);
      time = time < seekable.end(seekable.length - 1) ? time : seekable.end(seekable.length - 1);

      this.lastSeekTarget_ = time;
      this.trigger('seeking');
      this.el_.vjs_setProperty('currentTime', time);
      _Tech.prototype.setCurrentTime.call(this);
    }
  };

  /**
   * Get current time
   *
   * @param {Number=} time Current time of video
   * @return {Number} Current time
   * @method currentTime
   */

  Flash.prototype.currentTime = function currentTime(time) {
    // when seeking make the reported time keep up with the requested time
    // by reading the time we're seeking to
    if (this.seeking()) {
      return this.lastSeekTarget_ || 0;
    }
    return this.el_.vjs_getProperty('currentTime');
  };

  /**
   * Get current source
   *
   * @method currentSrc
   */

  Flash.prototype.currentSrc = function currentSrc() {
    if (this.currentSource_) {
      return this.currentSource_.src;
    } else {
      return this.el_.vjs_getProperty('currentSrc');
    }
  };

  /**
   * Load media into player
   *
   * @method load
   */

  Flash.prototype.load = function load() {
    this.el_.vjs_load();
  };

  /**
   * Determine if can seek in media
   *
   * @return {TimeRangeObject}
   * @method seekable
   */

  Flash.prototype.seekable = function seekable() {
    var duration = this.duration();
    if (duration === 0) {
      return _utilsTimeRangesJs.createTimeRange();
    }
    return _utilsTimeRangesJs.createTimeRange(0, duration);
  };

  /**
   * Get buffered time range
   *
   * @return {TimeRangeObject}
   * @method buffered
   */

  Flash.prototype.buffered = function buffered() {
    var ranges = this.el_.vjs_getProperty('buffered');
    if (ranges.length === 0) {
      return _utilsTimeRangesJs.createTimeRange();
    }
    return _utilsTimeRangesJs.createTimeRange(ranges[0][0], ranges[0][1]);
  };

  /**
   * Get fullscreen support -
   * Flash does not allow fullscreen through javascript
   * so always returns false
   *
   * @return {Boolean} false
   * @method supportsFullScreen
   */

  Flash.prototype.supportsFullScreen = function supportsFullScreen() {
    return false; // Flash does not allow fullscreen through javascript
  };

  /**
   * Request to enter fullscreen
   * Flash does not allow fullscreen through javascript
   * so always returns false
   *
   * @return {Boolean} false
   * @method enterFullScreen
   */

  Flash.prototype.enterFullScreen = function enterFullScreen() {
    return false;
  };

  return Flash;
})(_tech2['default']);

var _api = Flash.prototype;
var _readWrite = 'rtmpConnection,rtmpStream,preload,defaultPlaybackRate,playbackRate,autoplay,loop,mediaGroup,controller,controls,volume,muted,defaultMuted,poster'.split(',');
var _readOnly = 'networkState,readyState,initialTime,duration,startOffsetTime,paused,ended,videoWidth,videoHeight'.split(',');

function _createSetter(attr) {
  var attrUpper = attr.charAt(0).toUpperCase() + attr.slice(1);
  _api['set' + attrUpper] = function (val) {
    return this.el_.vjs_setProperty(attr, val);
  };
}
function _createGetter(attr) {
  _api[attr] = function () {
    return this.el_.vjs_getProperty(attr);
  };
}

// Create getter and setters for all read/write attributes
for (var i = 0; i < _readWrite.length; i++) {
  _createGetter(_readWrite[i]);
  _createSetter(_readWrite[i]);
}

// Create getters for read-only attributes
for (var i = 0; i < _readOnly.length; i++) {
  _createGetter(_readOnly[i]);
}

/* Flash Support Testing -------------------------------------------------------- */

Flash.isSupported = function () {
  return Flash.version()[0] >= 10;
  // return swfobject.hasFlashPlayerVersion('10');
};

// Add Source Handler pattern functions to this tech
_tech2['default'].withSourceHandlers(Flash);

/*
 * The default native source handler.
 * This simply passes the source to the video element. Nothing fancy.
 *
 * @param  {Object} source   The source object
 * @param  {Flash} tech  The instance of the Flash tech
 */
Flash.nativeSourceHandler = {};

/**
 * Check if Flash can play the given videotype
 * @param  {String} type    The mimetype to check
 * @return {String}         'probably', 'maybe', or '' (empty string)
 */
Flash.nativeSourceHandler.canPlayType = function (type) {
  if (type in Flash.formats) {
    return 'maybe';
  }

  return '';
};

/*
 * Check Flash can handle the source natively
 *
 * @param  {Object} source  The source object
 * @return {String}         'probably', 'maybe', or '' (empty string)
 */
Flash.nativeSourceHandler.canHandleSource = function (source) {
  var type;

  function guessMimeType(src) {
    var ext = Url.getFileExtension(src);
    if (ext) {
      return 'video/' + ext;
    }
    return '';
  }

  if (!source.type) {
    type = guessMimeType(source.src);
  } else {
    // Strip code information from the type because we don't get that specific
    type = source.type.replace(/;.*/, '').toLowerCase();
  }

  return Flash.nativeSourceHandler.canPlayType(type);
};

/*
 * Pass the source to the flash object
 * Adaptive source handlers will have more complicated workflows before passing
 * video data to the video element
 *
 * @param  {Object} source   The source object
 * @param  {Flash}  tech     The instance of the Flash tech
 * @param  {Object} options  The options to pass to the source
 */
Flash.nativeSourceHandler.handleSource = function (source, tech, options) {
  tech.setSrc(source.src);
};

/*
 * Clean up the source handler when disposing the player or switching sources..
 * (no cleanup is needed when supporting the format natively)
 */
Flash.nativeSourceHandler.dispose = function () {};

// Register the native source handler
Flash.registerSourceHandler(Flash.nativeSourceHandler);

Flash.formats = {
  'video/flv': 'FLV',
  'video/x-flv': 'FLV',
  'video/mp4': 'MP4',
  'video/m4v': 'MP4'
};

Flash.onReady = function (currSwf) {
  var el = Dom.getEl(currSwf);
  var tech = el && el.tech;

  // if there is no el then the tech has been disposed
  // and the tech element was removed from the player div
  if (tech && tech.el()) {
    // check that the flash object is really ready
    Flash.checkReady(tech);
  }
};

// The SWF isn't always ready when it says it is. Sometimes the API functions still need to be added to the object.
// If it's not ready, we set a timeout to check again shortly.
Flash.checkReady = function (tech) {
  // stop worrying if the tech has been disposed
  if (!tech.el()) {
    return;
  }

  // check if API property exists
  if (tech.el().vjs_getProperty) {
    // tell tech it's ready
    tech.triggerReady();
  } else {
    // wait longer
    this.setTimeout(function () {
      Flash['checkReady'](tech);
    }, 50);
  }
};

// Trigger events from the swf on the player
Flash.onEvent = function (swfID, eventName) {
  var tech = Dom.getEl(swfID).tech;
  tech.trigger(eventName);
};

// Log errors from the swf
Flash.onError = function (swfID, err) {
  var tech = Dom.getEl(swfID).tech;

  // trigger MEDIA_ERR_SRC_NOT_SUPPORTED
  if (err === 'srcnotfound') {
    return tech.error(4);
  }

  // trigger a custom error
  tech.error('FLASH: ' + err);
};

// Flash Version Check
Flash.version = function () {
  var version = '0,0,0';

  // IE
  try {
    version = new _globalWindow2['default'].ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version').replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];

    // other browsers
  } catch (e) {
    try {
      if (navigator.mimeTypes['application/x-shockwave-flash'].enabledPlugin) {
        version = (navigator.plugins['Shockwave Flash 2.0'] || navigator.plugins['Shockwave Flash']).description.replace(/\D+/g, ',').match(/^,?(.+),?$/)[1];
      }
    } catch (err) {}
  }
  return version.split(',');
};

// Flash embedding method. Only used in non-iframe mode
Flash.embed = function (swf, flashVars, params, attributes) {
  var code = Flash.getEmbedCode(swf, flashVars, params, attributes);

  // Get element by embedding code and retrieving created element
  var obj = Dom.createEl('div', { innerHTML: code }).childNodes[0];

  return obj;
};

Flash.getEmbedCode = function (swf, flashVars, params, attributes) {
  var objTag = '<object type="application/x-shockwave-flash" ';
  var flashVarsString = '';
  var paramsString = '';
  var attrsString = '';

  // Convert flash vars to string
  if (flashVars) {
    Object.getOwnPropertyNames(flashVars).forEach(function (key) {
      flashVarsString += key + '=' + flashVars[key] + '&amp;';
    });
  }

  // Add swf, flashVars, and other default params
  params = _objectAssign2['default']({
    'movie': swf,
    'flashvars': flashVarsString,
    'allowScriptAccess': 'always', // Required to talk to swf
    'allowNetworking': 'all' // All should be default, but having security issues.
  }, params);

  // Create param tags string
  Object.getOwnPropertyNames(params).forEach(function (key) {
    paramsString += '<param name="' + key + '" value="' + params[key] + '" />';
  });

  attributes = _objectAssign2['default']({
    // Add swf to attributes (need both for IE and Others to work)
    'data': swf,

    // Default to 100% width/height
    'width': '100%',
    'height': '100%'

  }, attributes);

  // Create Attributes string
  Object.getOwnPropertyNames(attributes).forEach(function (key) {
    attrsString += key + '="' + attributes[key] + '" ';
  });

  return '' + objTag + attrsString + '>' + paramsString + '</object>';
};

// Run Flash through the RTMP decorator
_flashRtmp2['default'](Flash);

_component2['default'].registerComponent('Flash', Flash);
_tech2['default'].registerTech('Flash', Flash);
exports['default'] = Flash;
module.exports = exports['default'];

},{"../component":66,"../utils/dom.js":144,"../utils/time-ranges.js":152,"../utils/url.js":154,"./flash-rtmp":120,"./tech":124,"global/window":8,"object.assign":53}],122:[function(_dereq_,module,exports){
/**
 * @file html5.js
 * HTML5 Media Controller - Wrapper for HTML5 Media API
 */

'use strict';

exports.__esModule = true;

var _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.']);

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

function _taggedTemplateLiteralLoose(strings, raw) { strings.raw = raw; return strings; }

var _techJs = _dereq_('./tech.js');

var _techJs2 = _interopRequireDefault(_techJs);

var _component = _dereq_('../component');

var _component2 = _interopRequireDefault(_component);

var _utilsDomJs = _dereq_('../utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsUrlJs = _dereq_('../utils/url.js');

var Url = _interopRequireWildcard(_utilsUrlJs);

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsLogJs = _dereq_('../utils/log.js');

var _utilsLogJs2 = _interopRequireDefault(_utilsLogJs);

var _tsml = _dereq_('tsml');

var _tsml2 = _interopRequireDefault(_tsml);

var _srcJsTracksTextTrackJs = _dereq_('../../../src/js/tracks/text-track.js');

var _srcJsTracksTextTrackJs2 = _interopRequireDefault(_srcJsTracksTextTrackJs);

var _utilsBrowserJs = _dereq_('../utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _objectAssign = _dereq_('object.assign');

var _objectAssign2 = _interopRequireDefault(_objectAssign);

var _utilsMergeOptionsJs = _dereq_('../utils/merge-options.js');

var _utilsMergeOptionsJs2 = _interopRequireDefault(_utilsMergeOptionsJs);

var _utilsToTitleCaseJs = _dereq_('../utils/to-title-case.js');

var _utilsToTitleCaseJs2 = _interopRequireDefault(_utilsToTitleCaseJs);

/**
 * HTML5 Media Controller - Wrapper for HTML5 Media API
 *
 * @param {Object=} options Object of option names and values
 * @param {Function=} ready Ready callback function
 * @extends Tech
 * @class Html5
 */

var Html5 = (function (_Tech) {
  _inherits(Html5, _Tech);

  function Html5(options, ready) {
    var _this = this;

    _classCallCheck(this, Html5);

    _Tech.call(this, options, ready);

    var source = options.source;
    var crossoriginTracks = false;

    // Set the source if one is provided
    // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
    // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
    // anyway so the error gets fired.
    if (source && (this.el_.currentSrc !== source.src || options.tag && options.tag.initNetworkState_ === 3)) {
      this.setSource(source);
    } else {
      this.handleLateInit_(this.el_);
    }

    if (this.el_.hasChildNodes()) {

      var nodes = this.el_.childNodes;
      var nodesLength = nodes.length;
      var removeNodes = [];

      while (nodesLength--) {
        var node = nodes[nodesLength];
        var nodeName = node.nodeName.toLowerCase();

        if (nodeName === 'track') {
          if (!this.featuresNativeTextTracks) {
            // Empty video tag tracks so the built-in player doesn't use them also.
            // This may not be fast enough to stop HTML5 browsers from reading the tags
            // so we'll need to turn off any default tracks if we're manually doing
            // captions and subtitles. videoElement.textTracks
            removeNodes.push(node);
          } else {
            // store HTMLTrackElement and TextTrack to remote list
            this.remoteTextTrackEls().addTrackElement_(node);
            this.remoteTextTracks().addTrack_(node.track);
            if (!crossoriginTracks && !this.el_.hasAttribute('crossorigin') && Url.isCrossOrigin(node.src)) {
              crossoriginTracks = true;
            }
          }
        }
      }

      for (var i = 0; i < removeNodes.length; i++) {
        this.el_.removeChild(removeNodes[i]);
      }
    }

    var trackTypes = ['audio', 'video'];

    // ProxyNativeTextTracks
    trackTypes.forEach(function (type) {
      var capitalType = _utilsToTitleCaseJs2['default'](type);

      if (!_this['featuresNative' + capitalType + 'Tracks']) {
        return;
      }
      var tl = _this.el()[type + 'Tracks'];

      if (tl && tl.addEventListener) {
        tl.addEventListener('change', Fn.bind(_this, _this['handle' + capitalType + 'TrackChange_']));
        tl.addEventListener('addtrack', Fn.bind(_this, _this['handle' + capitalType + 'TrackAdd_']));
        tl.addEventListener('removetrack', Fn.bind(_this, _this['handle' + capitalType + 'TrackRemove_']));
      }
    });

    if (this.featuresNativeTextTracks) {
      if (crossoriginTracks) {
        _utilsLogJs2['default'].warn(_tsml2['default'](_templateObject));
      }

      this.handleTextTrackChange_ = Fn.bind(this, this.handleTextTrackChange);
      this.handleTextTrackAdd_ = Fn.bind(this, this.handleTextTrackAdd);
      this.handleTextTrackRemove_ = Fn.bind(this, this.handleTextTrackRemove);
      this.proxyNativeTextTracks_();
    }

    // Determine if native controls should be used
    // Our goal should be to get the custom controls on mobile solid everywhere
    // so we can remove this all together. Right now this will block custom
    // controls on touch enabled laptops like the Chrome Pixel
    if ((browser.TOUCH_ENABLED || browser.IS_IPHONE || browser.IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
      this.setControls(true);
    }

    this.triggerReady();
  }

  /* HTML5 Support Testing ---------------------------------------------------- */

  /*
  * Element for testing browser HTML5 video capabilities
  *
  * @type {Element}
  * @constant
  * @private
  */

  /**
   * Dispose of html5 media element
   *
   * @method dispose
   */

  Html5.prototype.dispose = function dispose() {
    var _this2 = this;

    // Un-ProxyNativeTracks
    ['audio', 'video', 'text'].forEach(function (type) {
      var capitalType = _utilsToTitleCaseJs2['default'](type);
      var tl = _this2.el_[type + 'Tracks'];

      if (tl && tl.removeEventListener) {
        tl.removeEventListener('change', _this2['handle' + capitalType + 'TrackChange_']);
        tl.removeEventListener('addtrack', _this2['handle' + capitalType + 'TrackAdd_']);
        tl.removeEventListener('removetrack', _this2['handle' + capitalType + 'TrackRemove_']);
      }
    });

    Html5.disposeMediaElement(this.el_);
    // tech will handle clearing of the emulated track list
    _Tech.prototype.dispose.call(this);
  };

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  Html5.prototype.createEl = function createEl() {
    var el = this.options_.tag;

    // Check if this browser supports moving the element into the box.
    // On the iPhone video will break if you move the element,
    // So we have to create a brand new element.
    if (!el || this['movingMediaElementInDOM'] === false) {

      // If the original tag is still there, clone and remove it.
      if (el) {
        var clone = el.cloneNode(true);
        el.parentNode.insertBefore(clone, el);
        Html5.disposeMediaElement(el);
        el = clone;
      } else {
        el = _globalDocument2['default'].createElement('video');

        // determine if native controls should be used
        var tagAttributes = this.options_.tag && Dom.getElAttributes(this.options_.tag);
        var attributes = _utilsMergeOptionsJs2['default']({}, tagAttributes);
        if (!browser.TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
          delete attributes.controls;
        }

        Dom.setElAttributes(el, _objectAssign2['default'](attributes, {
          id: this.options_.techId,
          'class': 'vjs-tech'
        }));
      }
    }

    // Update specific tag settings, in case they were overridden
    var settingsAttrs = ['autoplay', 'preload', 'loop', 'muted', 'playsinline'];
    for (var i = settingsAttrs.length - 1; i >= 0; i--) {
      var attr = settingsAttrs[i];
      var overwriteAttrs = {};
      if (typeof this.options_[attr] !== 'undefined') {
        overwriteAttrs[attr] = this.options_[attr];
      }
      Dom.setElAttributes(el, overwriteAttrs);
    }

    return el;
    // jenniisawesome = true;
  };

  // If we're loading the playback object after it has started loading
  // or playing the video (often with autoplay on) then the loadstart event
  // has already fired and we need to fire it manually because many things
  // rely on it.

  Html5.prototype.handleLateInit_ = function handleLateInit_(el) {
    var _this3 = this;

    if (el.networkState === 0 || el.networkState === 3) {
      // The video element hasn't started loading the source yet
      // or didn't find a source
      return;
    }

    if (el.readyState === 0) {
      var _ret = (function () {
        // NetworkState is set synchronously BUT loadstart is fired at the
        // end of the current stack, usually before setInterval(fn, 0).
        // So at this point we know loadstart may have already fired or is
        // about to fire, and either way the player hasn't seen it yet.
        // We don't want to fire loadstart prematurely here and cause a
        // double loadstart so we'll wait and see if it happens between now
        // and the next loop, and fire it if not.
        // HOWEVER, we also want to make sure it fires before loadedmetadata
        // which could also happen between now and the next loop, so we'll
        // watch for that also.
        var loadstartFired = false;
        var setLoadstartFired = function setLoadstartFired() {
          loadstartFired = true;
        };
        _this3.on('loadstart', setLoadstartFired);

        var triggerLoadstart = function triggerLoadstart() {
          // We did miss the original loadstart. Make sure the player
          // sees loadstart before loadedmetadata
          if (!loadstartFired) {
            this.trigger('loadstart');
          }
        };
        _this3.on('loadedmetadata', triggerLoadstart);

        _this3.ready(function () {
          this.off('loadstart', setLoadstartFired);
          this.off('loadedmetadata', triggerLoadstart);

          if (!loadstartFired) {
            // We did miss the original native loadstart. Fire it now.
            this.trigger('loadstart');
          }
        });

        return {
          v: undefined
        };
      })();

      if (typeof _ret === 'object') return _ret.v;
    }

    // From here on we know that loadstart already fired and we missed it.
    // The other readyState events aren't as much of a problem if we double
    // them, so not going to go to as much trouble as loadstart to prevent
    // that unless we find reason to.
    var eventsToTrigger = ['loadstart'];

    // loadedmetadata: newly equal to HAVE_METADATA (1) or greater
    eventsToTrigger.push('loadedmetadata');

    // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater
    if (el.readyState >= 2) {
      eventsToTrigger.push('loadeddata');
    }

    // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater
    if (el.readyState >= 3) {
      eventsToTrigger.push('canplay');
    }

    // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4)
    if (el.readyState >= 4) {
      eventsToTrigger.push('canplaythrough');
    }

    // We still need to give the player time to add event listeners
    this.ready(function () {
      eventsToTrigger.forEach(function (type) {
        this.trigger(type);
      }, this);
    });
  };

  Html5.prototype.proxyNativeTextTracks_ = function proxyNativeTextTracks_() {
    var tt = this.el().textTracks;

    if (tt) {
      // Add tracks - if player is initialised after DOM loaded, textTracks
      // will not trigger addtrack
      for (var i = 0; i < tt.length; i++) {
        this.textTracks().addTrack_(tt[i]);
      }

      if (tt.addEventListener) {
        tt.addEventListener('change', this.handleTextTrackChange_);
        tt.addEventListener('addtrack', this.handleTextTrackAdd_);
        tt.addEventListener('removetrack', this.handleTextTrackRemove_);
      }
    }
  };

  Html5.prototype.handleTextTrackChange = function handleTextTrackChange(e) {
    var tt = this.textTracks();
    this.textTracks().trigger({
      type: 'change',
      target: tt,
      currentTarget: tt,
      srcElement: tt
    });
  };

  Html5.prototype.handleTextTrackAdd = function handleTextTrackAdd(e) {
    this.textTracks().addTrack_(e.track);
  };

  Html5.prototype.handleTextTrackRemove = function handleTextTrackRemove(e) {
    this.textTracks().removeTrack_(e.track);
  };

  Html5.prototype.handleVideoTrackChange_ = function handleVideoTrackChange_(e) {
    var vt = this.videoTracks();
    this.videoTracks().trigger({
      type: 'change',
      target: vt,
      currentTarget: vt,
      srcElement: vt
    });
  };

  Html5.prototype.handleVideoTrackAdd_ = function handleVideoTrackAdd_(e) {
    this.videoTracks().addTrack_(e.track);
  };

  Html5.prototype.handleVideoTrackRemove_ = function handleVideoTrackRemove_(e) {
    this.videoTracks().removeTrack_(e.track);
  };

  Html5.prototype.handleAudioTrackChange_ = function handleAudioTrackChange_(e) {
    var audioTrackList = this.audioTracks();
    this.audioTracks().trigger({
      type: 'change',
      target: audioTrackList,
      currentTarget: audioTrackList,
      srcElement: audioTrackList
    });
  };

  Html5.prototype.handleAudioTrackAdd_ = function handleAudioTrackAdd_(e) {
    this.audioTracks().addTrack_(e.track);
  };

  Html5.prototype.handleAudioTrackRemove_ = function handleAudioTrackRemove_(e) {
    this.audioTracks().removeTrack_(e.track);
  };

  /**
   * Play for html5 tech
   *
   * @method play
   */

  Html5.prototype.play = function play() {
    var playPromise = this.el_.play();

    // Catch/silence error when a pause interrupts a play request
    // on browsers which return a promise
    if (playPromise !== undefined && typeof playPromise.then === 'function') {
      playPromise.then(null, function (e) {});
    }
  };

  /**
   * Pause for html5 tech
   *
   * @method pause
   */

  Html5.prototype.pause = function pause() {
    this.el_.pause();
  };

  /**
   * Paused for html5 tech
   *
   * @return {Boolean}
   * @method paused
   */

  Html5.prototype.paused = function paused() {
    return this.el_.paused;
  };

  /**
   * Get current time
   *
   * @return {Number}
   * @method currentTime
   */

  Html5.prototype.currentTime = function currentTime() {
    return this.el_.currentTime;
  };

  /**
   * Set current time
   *
   * @param {Number} seconds Current time of video
   * @method setCurrentTime
   */

  Html5.prototype.setCurrentTime = function setCurrentTime(seconds) {
    try {
      this.el_.currentTime = seconds;
    } catch (e) {
      _utilsLogJs2['default'](e, 'Video is not ready. (Video.js)');
      // this.warning(VideoJS.warnings.videoNotReady);
    }
  };

  /**
   * Get duration
   *
   * @return {Number}
   * @method duration
   */

  Html5.prototype.duration = function duration() {
    return this.el_.duration || 0;
  };

  /**
   * Get a TimeRange object that represents the intersection
   * of the time ranges for which the user agent has all
   * relevant media
   *
   * @return {TimeRangeObject}
   * @method buffered
   */

  Html5.prototype.buffered = function buffered() {
    return this.el_.buffered;
  };

  /**
   * Get volume level
   *
   * @return {Number}
   * @method volume
   */

  Html5.prototype.volume = function volume() {
    return this.el_.volume;
  };

  /**
   * Set volume level
   *
   * @param {Number} percentAsDecimal Volume percent as a decimal
   * @method setVolume
   */

  Html5.prototype.setVolume = function setVolume(percentAsDecimal) {
    this.el_.volume = percentAsDecimal;
  };

  /**
   * Get if muted
   *
   * @return {Boolean}
   * @method muted
   */

  Html5.prototype.muted = function muted() {
    return this.el_.muted;
  };

  /**
   * Set muted
   *
   * @param {Boolean} If player is to be muted or note
   * @method setMuted
   */

  Html5.prototype.setMuted = function setMuted(muted) {
    this.el_.muted = muted;
  };

  /**
   * Get player width
   *
   * @return {Number}
   * @method width
   */

  Html5.prototype.width = function width() {
    return this.el_.offsetWidth;
  };

  /**
   * Get player height
   *
   * @return {Number}
   * @method height
   */

  Html5.prototype.height = function height() {
    return this.el_.offsetHeight;
  };

  /**
   * Get if there is fullscreen support
   *
   * @return {Boolean}
   * @method supportsFullScreen
   */

  Html5.prototype.supportsFullScreen = function supportsFullScreen() {
    if (typeof this.el_.webkitEnterFullScreen === 'function') {
      var userAgent = _globalWindow2['default'].navigator.userAgent;
      // Seems to be broken in Chromium/Chrome && Safari in Leopard
      if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) {
        return true;
      }
    }
    return false;
  };

  /**
   * Request to enter fullscreen
   *
   * @method enterFullScreen
   */

  Html5.prototype.enterFullScreen = function enterFullScreen() {
    var video = this.el_;

    if ('webkitDisplayingFullscreen' in video) {
      this.one('webkitbeginfullscreen', function () {
        this.one('webkitendfullscreen', function () {
          this.trigger('fullscreenchange', { isFullscreen: false });
        });

        this.trigger('fullscreenchange', { isFullscreen: true });
      });
    }

    if (video.paused && video.networkState <= video.HAVE_METADATA) {
      // attempt to prime the video element for programmatic access
      // this isn't necessary on the desktop but shouldn't hurt
      this.play();

      // playing and pausing synchronously during the transition to fullscreen
      // can get iOS ~6.1 devices into a play/pause loop
      this.setTimeout(function () {
        video.pause();
        video.webkitEnterFullScreen();
      }, 0);
    } else {
      video.webkitEnterFullScreen();
    }
  };

  /**
   * Request to exit fullscreen
   *
   * @method exitFullScreen
   */

  Html5.prototype.exitFullScreen = function exitFullScreen() {
    this.el_.webkitExitFullScreen();
  };

  /**
   * Get/set video
   *
   * @param {Object=} src Source object
   * @return {Object}
   * @method src
   */

  Html5.prototype.src = function src(_src) {
    if (_src === undefined) {
      return this.el_.src;
    } else {
      // Setting src through `src` instead of `setSrc` will be deprecated
      this.setSrc(_src);
    }
  };

  /**
   * Set video
   *
   * @param {Object} src Source object
   * @deprecated
   * @method setSrc
   */

  Html5.prototype.setSrc = function setSrc(src) {
    this.el_.src = src;
  };

  /**
   * Load media into player
   *
   * @method load
   */

  Html5.prototype.load = function load() {
    this.el_.load();
  };

  /**
   * Reset the tech. Removes all sources and calls `load`.
   *
   * @method reset
   */

  Html5.prototype.reset = function reset() {
    Html5.resetMediaElement(this.el_);
  };

  /**
   * Get current source
   *
   * @return {Object}
   * @method currentSrc
   */

  Html5.prototype.currentSrc = function currentSrc() {
    if (this.currentSource_) {
      return this.currentSource_.src;
    } else {
      return this.el_.currentSrc;
    }
  };

  /**
   * Get poster
   *
   * @return {String}
   * @method poster
   */

  Html5.prototype.poster = function poster() {
    return this.el_.poster;
  };

  /**
   * Set poster
   *
   * @param {String} val URL to poster image
   * @method
   */

  Html5.prototype.setPoster = function setPoster(val) {
    this.el_.poster = val;
  };

  /**
   * Get preload attribute
   *
   * @return {String}
   * @method preload
   */

  Html5.prototype.preload = function preload() {
    return this.el_.preload;
  };

  /**
   * Set preload attribute
   *
   * @param {String} val Value for preload attribute
   * @method setPreload
   */

  Html5.prototype.setPreload = function setPreload(val) {
    this.el_.preload = val;
  };

  /**
   * Get autoplay attribute
   *
   * @return {String}
   * @method autoplay
   */

  Html5.prototype.autoplay = function autoplay() {
    return this.el_.autoplay;
  };

  /**
   * Set autoplay attribute
   *
   * @param {String} val Value for preload attribute
   * @method setAutoplay
   */

  Html5.prototype.setAutoplay = function setAutoplay(val) {
    this.el_.autoplay = val;
  };

  /**
   * Get controls attribute
   *
   * @return {String}
   * @method controls
   */

  Html5.prototype.controls = function controls() {
    return this.el_.controls;
  };

  /**
   * Set controls attribute
   *
   * @param {String} val Value for controls attribute
   * @method setControls
   */

  Html5.prototype.setControls = function setControls(val) {
    this.el_.controls = !!val;
  };

  /**
   * Get loop attribute
   *
   * @return {String}
   * @method loop
   */

  Html5.prototype.loop = function loop() {
    return this.el_.loop;
  };

  /**
   * Set loop attribute
   *
   * @param {String} val Value for loop attribute
   * @method setLoop
   */

  Html5.prototype.setLoop = function setLoop(val) {
    this.el_.loop = val;
  };

  /**
   * Get error value
   *
   * @return {String}
   * @method error
   */

  Html5.prototype.error = function error() {
    return this.el_.error;
  };

  /**
   * Get whether or not the player is in the "seeking" state
   *
   * @return {Boolean}
   * @method seeking
   */

  Html5.prototype.seeking = function seeking() {
    return this.el_.seeking;
  };

  /**
   * Get a TimeRanges object that represents the
   * ranges of the media resource to which it is possible
   * for the user agent to seek.
   *
   * @return {TimeRangeObject}
   * @method seekable
   */

  Html5.prototype.seekable = function seekable() {
    return this.el_.seekable;
  };

  /**
   * Get if video ended
   *
   * @return {Boolean}
   * @method ended
   */

  Html5.prototype.ended = function ended() {
    return this.el_.ended;
  };

  /**
   * Get the value of the muted content attribute
   * This attribute has no dynamic effect, it only
   * controls the default state of the element
   *
   * @return {Boolean}
   * @method defaultMuted
   */

  Html5.prototype.defaultMuted = function defaultMuted() {
    return this.el_.defaultMuted;
  };

  /**
   * Get desired speed at which the media resource is to play
   *
   * @return {Number}
   * @method playbackRate
   */

  Html5.prototype.playbackRate = function playbackRate() {
    return this.el_.playbackRate;
  };

  /**
   * Returns a TimeRanges object that represents the ranges of the
   * media resource that the user agent has played.
   * @return {TimeRangeObject} the range of points on the media
   * timeline that has been reached through normal playback
   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-played
   */

  Html5.prototype.played = function played() {
    return this.el_.played;
  };

  /**
   * Set desired speed at which the media resource is to play
   *
   * @param {Number} val Speed at which the media resource is to play
   * @method setPlaybackRate
   */

  Html5.prototype.setPlaybackRate = function setPlaybackRate(val) {
    this.el_.playbackRate = val;
  };

  /**
   * Get the current state of network activity for the element, from
   * the list below
   * NETWORK_EMPTY (numeric value 0)
   * NETWORK_IDLE (numeric value 1)
   * NETWORK_LOADING (numeric value 2)
   * NETWORK_NO_SOURCE (numeric value 3)
   *
   * @return {Number}
   * @method networkState
   */

  Html5.prototype.networkState = function networkState() {
    return this.el_.networkState;
  };

  /**
   * Get a value that expresses the current state of the element
   * with respect to rendering the current playback position, from
   * the codes in the list below
   * HAVE_NOTHING (numeric value 0)
   * HAVE_METADATA (numeric value 1)
   * HAVE_CURRENT_DATA (numeric value 2)
   * HAVE_FUTURE_DATA (numeric value 3)
   * HAVE_ENOUGH_DATA (numeric value 4)
   *
   * @return {Number}
   * @method readyState
   */

  Html5.prototype.readyState = function readyState() {
    return this.el_.readyState;
  };

  /**
   * Get width of video
   *
   * @return {Number}
   * @method videoWidth
   */

  Html5.prototype.videoWidth = function videoWidth() {
    return this.el_.videoWidth;
  };

  /**
   * Get height of video
   *
   * @return {Number}
   * @method videoHeight
   */

  Html5.prototype.videoHeight = function videoHeight() {
    return this.el_.videoHeight;
  };

  /**
   * Get text tracks
   *
   * @return {TextTrackList}
   * @method textTracks
   */

  Html5.prototype.textTracks = function textTracks() {
    return _Tech.prototype.textTracks.call(this);
  };

  /**
   * Creates and returns a text track object
   *
   * @param {String} kind Text track kind (subtitles, captions, descriptions
   *                                       chapters and metadata)
   * @param {String=} label Label to identify the text track
   * @param {String=} language Two letter language abbreviation
   * @return {TextTrackObject}
   * @method addTextTrack
   */

  Html5.prototype.addTextTrack = function addTextTrack(kind, label, language) {
    if (!this['featuresNativeTextTracks']) {
      return _Tech.prototype.addTextTrack.call(this, kind, label, language);
    }

    return this.el_.addTextTrack(kind, label, language);
  };

  /**
   * Creates a remote text track object and returns a html track element
   *
   * @param {Object} options The object should contain values for
   * kind, language, label and src (location of the WebVTT file)
   * @return {HTMLTrackElement}
   * @method addRemoteTextTrack
   */

  Html5.prototype.addRemoteTextTrack = function addRemoteTextTrack() {
    var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

    if (!this['featuresNativeTextTracks']) {
      return _Tech.prototype.addRemoteTextTrack.call(this, options);
    }

    var htmlTrackElement = _globalDocument2['default'].createElement('track');

    if (options.kind) {
      htmlTrackElement.kind = options.kind;
    }
    if (options.label) {
      htmlTrackElement.label = options.label;
    }
    if (options.language || options.srclang) {
      htmlTrackElement.srclang = options.language || options.srclang;
    }
    if (options['default']) {
      htmlTrackElement['default'] = options['default'];
    }
    if (options.id) {
      htmlTrackElement.id = options.id;
    }
    if (options.src) {
      htmlTrackElement.src = options.src;
    }

    this.el().appendChild(htmlTrackElement);

    // store HTMLTrackElement and TextTrack to remote list
    this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
    this.remoteTextTracks().addTrack_(htmlTrackElement.track);

    return htmlTrackElement;
  };

  /**
   * Remove remote text track from TextTrackList object
   *
   * @param {TextTrackObject} track Texttrack object to remove
   * @method removeRemoteTextTrack
   */

  Html5.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
    if (!this['featuresNativeTextTracks']) {
      return _Tech.prototype.removeRemoteTextTrack.call(this, track);
    }

    var tracks = undefined,
        i = undefined;

    var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track);

    // remove HTMLTrackElement and TextTrack from remote list
    this.remoteTextTrackEls().removeTrackElement_(trackElement);
    this.remoteTextTracks().removeTrack_(track);

    tracks = this.$$('track');

    i = tracks.length;
    while (i--) {
      if (track === tracks[i] || track === tracks[i].track) {
        this.el().removeChild(tracks[i]);
      }
    }
  };

  /**
   * Get the value of `playsinline` from the media element. `playsinline` indicates
   * to the browser that non-fullscreen playback is preferred when fullscreen
   * playback is the native default, such as in iOS Safari.
   *
   * @method Html5#playsinline
   * @return {boolean}
   *         - The value of `playsinline` from the media element.
   *         - True indicates that the media should play inline.
   *         - False indicates that the media should not play inline.
   *
   * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
   */

  Html5.prototype.playsinline = function playsinline() {
    return this.el_.hasAttribute('playsinline');
  };

  /**
   * Set the value of `playsinline` from the media element. `playsinline` indicates
   * to the browser that non-fullscreen playback is preferred when fullscreen
   * playback is the native default, such as in iOS Safari.
   *
   * @method Html5#setPlaysinline
   * @param {boolean} playsinline
   *         - True indicates that the media should play inline.
   *         - False indicates that the media should not play inline.
   *
   * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
   */

  Html5.prototype.setPlaysinline = function setPlaysinline(value) {
    if (value) {
      this.el_.setAttribute('playsinline', 'playsinline');
    } else {
      this.el_.removeAttribute('playsinline');
    }
  };

  return Html5;
})(_techJs2['default']);

Html5.TEST_VID = _globalDocument2['default'].createElement('video');
var track = _globalDocument2['default'].createElement('track');
track.kind = 'captions';
track.srclang = 'en';
track.label = 'English';
Html5.TEST_VID.appendChild(track);

/*
 * Check if HTML5 video is supported by this browser/device
 *
 * @return {Boolean}
 */
Html5.isSupported = function () {
  // IE9 with no Media Player is a LIAR! (#984)
  try {
    Html5.TEST_VID['volume'] = 0.5;
  } catch (e) {
    return false;
  }

  return !!Html5.TEST_VID.canPlayType;
};

// Add Source Handler pattern functions to this tech
_techJs2['default'].withSourceHandlers(Html5);

/*
 * The default native source handler.
 * This simply passes the source to the video element. Nothing fancy.
 *
 * @param  {Object} source   The source object
 * @param  {Html5} tech  The instance of the HTML5 tech
 */
Html5.nativeSourceHandler = {};

/*
 * Check if the video element can play the given videotype
 *
 * @param  {String} type    The mimetype to check
 * @return {String}         'probably', 'maybe', or '' (empty string)
 */
Html5.nativeSourceHandler.canPlayType = function (type) {
  // IE9 on Windows 7 without MediaPlayer throws an error here
  // https://github.com/videojs/video.js/issues/519
  try {
    return Html5.TEST_VID.canPlayType(type);
  } catch (e) {
    return '';
  }
};

/*
 * Check if the video element can handle the source natively
 *
 * @param  {Object} source  The source object
 * @return {String}         'probably', 'maybe', or '' (empty string)
 */
Html5.nativeSourceHandler.canHandleSource = function (source) {
  var match, ext;

  // If a type was provided we should rely on that
  if (source.type) {
    return Html5.nativeSourceHandler.canPlayType(source.type);
  } else if (source.src) {
    // If no type, fall back to checking 'video/[EXTENSION]'
    ext = Url.getFileExtension(source.src);

    return Html5.nativeSourceHandler.canPlayType('video/' + ext);
  }

  return '';
};

/*
 * Pass the source to the video element
 * Adaptive source handlers will have more complicated workflows before passing
 * video data to the video element
 *
 * @param  {Object} source   The source object
 * @param  {Html5}  tech     The instance of the Html5 tech
 * @param  {Object} options  The options to pass to the source
 */
Html5.nativeSourceHandler.handleSource = function (source, tech, options) {
  tech.setSrc(source.src);
};

/*
* Clean up the source handler when disposing the player or switching sources..
* (no cleanup is needed when supporting the format natively)
*/
Html5.nativeSourceHandler.dispose = function () {};

// Register the native source handler
Html5.registerSourceHandler(Html5.nativeSourceHandler);

/*
 * Check if the volume can be changed in this browser/device.
 * Volume cannot be changed in a lot of mobile devices.
 * Specifically, it can't be changed from 1 on iOS.
 *
 * @return {Boolean}
 */
Html5.canControlVolume = function () {
  var volume = Html5.TEST_VID.volume;
  Html5.TEST_VID.volume = volume / 2 + 0.1;
  return volume !== Html5.TEST_VID.volume;
};

/*
 * Check if the muted can be changed in this browser/device.
 *
 * @return {Boolean}
 */
Html5.canControlMuted = function () {
  Html5.TEST_VID.muted = false;
  if (Html5.TEST_VID.muted) {
    return false;
  }
  Html5.TEST_VID.muted = true;
  return Html5.TEST_VID.muted;
};

/*
 * Check if playbackRate is supported in this browser/device.
 *
 * @return {Boolean}
 */
Html5.canControlPlaybackRate = function () {
  var playbackRate = Html5.TEST_VID.playbackRate;
  Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1;
  return playbackRate !== Html5.TEST_VID.playbackRate;
};

/*
 * Check to see if native text tracks are supported by this browser/device
 *
 * @return {Boolean}
 */
Html5.supportsNativeTextTracks = function () {
  return browser.IS_ANY_SAFARI;
};

/*
 * Check to see if native video tracks are supported by this browser/device
 *
 * @return {Boolean}
 */
Html5.supportsNativeVideoTracks = function () {
  var supportsVideoTracks = !!Html5.TEST_VID.videoTracks;
  return supportsVideoTracks;
};

/*
 * Check to see if native audio tracks are supported by this browser/device
 *
 * @return {Boolean}
 */
Html5.supportsNativeAudioTracks = function () {
  var supportsAudioTracks = !!Html5.TEST_VID.audioTracks;
  return supportsAudioTracks;
};

/**
 * An array of events available on the Html5 tech.
 *
 * @private
 * @type {Array}
 */
Html5.Events = ['loadstart', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'waiting', 'seeking', 'seeked', 'ended', 'durationchange', 'timeupdate', 'progress', 'play', 'pause', 'ratechange', 'volumechange'];

/*
 * Set the tech's volume control support status
 *
 * @type {Boolean}
 */
Html5.prototype['featuresVolumeControl'] = Html5.canControlVolume();

/*
 * Set the tech's muted control support status
 *
 * @type {Boolean}
 */
Html5.prototype['featuresMutedControl'] = Html5.canControlMuted();

/*
 * Set the tech's playbackRate support status
 *
 * @type {Boolean}
 */
Html5.prototype['featuresPlaybackRate'] = Html5.canControlPlaybackRate();

/*
 * Set the tech's status on moving the video element.
 * In iOS, if you move a video element in the DOM, it breaks video playback.
 *
 * @type {Boolean}
 */
Html5.prototype['movingMediaElementInDOM'] = !browser.IS_IOS;

/*
 * Set the the tech's fullscreen resize support status.
 * HTML video is able to automatically resize when going to fullscreen.
 * (No longer appears to be used. Can probably be removed.)
 */
Html5.prototype['featuresFullscreenResize'] = true;

/*
 * Set the tech's progress event support status
 * (this disables the manual progress events of the Tech)
 */
Html5.prototype['featuresProgressEvents'] = true;

/*
 * Sets the tech's status on native text track support
 *
 * @type {Boolean}
 */
Html5.prototype['featuresNativeTextTracks'] = Html5.supportsNativeTextTracks();

/**
 * Sets the tech's status on native text track support
 *
 * @type {Boolean}
 */
Html5.prototype['featuresNativeVideoTracks'] = Html5.supportsNativeVideoTracks();

/**
 * Sets the tech's status on native audio track support
 *
 * @type {Boolean}
 */
Html5.prototype['featuresNativeAudioTracks'] = Html5.supportsNativeAudioTracks();

// HTML5 Feature detection and Device Fixes --------------------------------- //
var canPlayType = undefined;
var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
var mp4RE = /^video\/mp4/i;

Html5.patchCanPlayType = function () {
  // Android 4.0 and above can play HLS to some extent but it reports being unable to do so
  if (browser.ANDROID_VERSION >= 4.0) {
    if (!canPlayType) {
      canPlayType = Html5.TEST_VID.constructor.prototype.canPlayType;
    }

    Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
      if (type && mpegurlRE.test(type)) {
        return 'maybe';
      }
      return canPlayType.call(this, type);
    };
  }

  // Override Android 2.2 and less canPlayType method which is broken
  if (browser.IS_OLD_ANDROID) {
    if (!canPlayType) {
      canPlayType = Html5.TEST_VID.constructor.prototype.canPlayType;
    }

    Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
      if (type && mp4RE.test(type)) {
        return 'maybe';
      }
      return canPlayType.call(this, type);
    };
  }
};

Html5.unpatchCanPlayType = function () {
  var r = Html5.TEST_VID.constructor.prototype.canPlayType;
  Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
  canPlayType = null;
  return r;
};

// by default, patch the video element
Html5.patchCanPlayType();

Html5.disposeMediaElement = function (el) {
  if (!el) {
    return;
  }

  if (el.parentNode) {
    el.parentNode.removeChild(el);
  }

  // remove any child track or source nodes to prevent their loading
  while (el.hasChildNodes()) {
    el.removeChild(el.firstChild);
  }

  // remove any src reference. not setting `src=''` because that causes a warning
  // in firefox
  el.removeAttribute('src');

  // force the media element to update its loading state by calling load()
  // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793)
  if (typeof el.load === 'function') {
    // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
    (function () {
      try {
        el.load();
      } catch (e) {
        // not supported
      }
    })();
  }
};

Html5.resetMediaElement = function (el) {
  if (!el) {
    return;
  }

  var sources = el.querySelectorAll('source');
  var i = sources.length;
  while (i--) {
    el.removeChild(sources[i]);
  }

  // remove any src reference.
  // not setting `src=''` because that throws an error
  el.removeAttribute('src');

  if (typeof el.load === 'function') {
    // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
    (function () {
      try {
        el.load();
      } catch (e) {}
    })();
  }
};

_component2['default'].registerComponent('Html5', Html5);
_techJs2['default'].registerTech('Html5', Html5);
exports['default'] = Html5;
module.exports = exports['default'];

},{"../../../src/js/tracks/text-track.js":135,"../component":66,"../utils/browser.js":141,"../utils/dom.js":144,"../utils/fn.js":146,"../utils/log.js":149,"../utils/merge-options.js":150,"../utils/to-title-case.js":153,"../utils/url.js":154,"./tech.js":124,"global/document":7,"global/window":8,"object.assign":53,"tsml":59}],123:[function(_dereq_,module,exports){
/**
 * @file loader.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _componentJs = _dereq_('../component.js');

var _componentJs2 = _interopRequireDefault(_componentJs);

var _techJs = _dereq_('./tech.js');

var _techJs2 = _interopRequireDefault(_techJs);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _utilsToTitleCaseJs = _dereq_('../utils/to-title-case.js');

var _utilsToTitleCaseJs2 = _interopRequireDefault(_utilsToTitleCaseJs);

/**
 * The Media Loader is the component that decides which playback technology to load
 * when the player is initialized.
 *
 * @param {Object} player  Main Player
 * @param {Object=} options Object of option names and values
 * @param {Function=} ready    Ready callback function
 * @extends Component
 * @class MediaLoader
 */

var MediaLoader = (function (_Component) {
  _inherits(MediaLoader, _Component);

  function MediaLoader(player, options, ready) {
    _classCallCheck(this, MediaLoader);

    _Component.call(this, player, options, ready);

    // If there are no sources when the player is initialized,
    // load the first supported playback technology.

    if (!options.playerOptions['sources'] || options.playerOptions['sources'].length === 0) {
      for (var i = 0, j = options.playerOptions['techOrder']; i < j.length; i++) {
        var techName = _utilsToTitleCaseJs2['default'](j[i]);
        var tech = _techJs2['default'].getTech(techName);
        // Support old behavior of techs being registered as components.
        // Remove once that deprecated behavior is removed.
        if (!techName) {
          tech = _componentJs2['default'].getComponent(techName);
        }

        // Check if the browser supports this technology
        if (tech && tech.isSupported()) {
          player.loadTech_(techName);
          break;
        }
      }
    } else {
      // // Loop through playback technologies (HTML5, Flash) and check for support.
      // // Then load the best source.
      // // A few assumptions here:
      // //   All playback technologies respect preload false.
      player.src(options.playerOptions['sources']);
    }
  }

  return MediaLoader;
})(_componentJs2['default']);

_componentJs2['default'].registerComponent('MediaLoader', MediaLoader);
exports['default'] = MediaLoader;
module.exports = exports['default'];

},{"../component.js":66,"../utils/to-title-case.js":153,"./tech.js":124,"global/window":8}],124:[function(_dereq_,module,exports){
/**
 * @file tech.js
 * Media Technology Controller - Base class for media playback
 * technology controllers like Flash and HTML5
 */

'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _component = _dereq_('../component');

var _component2 = _interopRequireDefault(_component);

var _tracksHtmlTrackElement = _dereq_('../tracks/html-track-element');

var _tracksHtmlTrackElement2 = _interopRequireDefault(_tracksHtmlTrackElement);

var _tracksHtmlTrackElementList = _dereq_('../tracks/html-track-element-list');

var _tracksHtmlTrackElementList2 = _interopRequireDefault(_tracksHtmlTrackElementList);

var _utilsMergeOptionsJs = _dereq_('../utils/merge-options.js');

var _utilsMergeOptionsJs2 = _interopRequireDefault(_utilsMergeOptionsJs);

var _tracksTextTrack = _dereq_('../tracks/text-track');

var _tracksTextTrack2 = _interopRequireDefault(_tracksTextTrack);

var _tracksTextTrackList = _dereq_('../tracks/text-track-list');

var _tracksTextTrackList2 = _interopRequireDefault(_tracksTextTrackList);

var _tracksVideoTrack = _dereq_('../tracks/video-track');

var _tracksVideoTrack2 = _interopRequireDefault(_tracksVideoTrack);

var _tracksVideoTrackList = _dereq_('../tracks/video-track-list');

var _tracksVideoTrackList2 = _interopRequireDefault(_tracksVideoTrackList);

var _tracksAudioTrackList = _dereq_('../tracks/audio-track-list');

var _tracksAudioTrackList2 = _interopRequireDefault(_tracksAudioTrackList);

var _tracksAudioTrack = _dereq_('../tracks/audio-track');

var _tracksAudioTrack2 = _interopRequireDefault(_tracksAudioTrack);

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsLogJs = _dereq_('../utils/log.js');

var _utilsLogJs2 = _interopRequireDefault(_utilsLogJs);

var _utilsTimeRangesJs = _dereq_('../utils/time-ranges.js');

var _utilsBufferJs = _dereq_('../utils/buffer.js');

var _mediaErrorJs = _dereq_('../media-error.js');

var _mediaErrorJs2 = _interopRequireDefault(_mediaErrorJs);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

/**
 * Base class for media (HTML5 Video, Flash) controllers
 *
 * @param {Object=} options Options object
 * @param {Function=} ready Ready callback function
 * @extends Component
 * @class Tech
 */

var Tech = (function (_Component) {
  _inherits(Tech, _Component);

  function Tech() {
    var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
    var ready = arguments.length <= 1 || arguments[1] === undefined ? function () {} : arguments[1];

    _classCallCheck(this, Tech);

    // we don't want the tech to report user activity automatically.
    // This is done manually in addControlsListeners
    options.reportTouchActivity = false;
    _Component.call(this, null, options, ready);

    // keep track of whether the current source has played at all to
    // implement a very limited played()
    this.hasStarted_ = false;
    this.on('playing', function () {
      this.hasStarted_ = true;
    });
    this.on('loadstart', function () {
      this.hasStarted_ = false;
    });

    this.textTracks_ = options.textTracks;
    this.videoTracks_ = options.videoTracks;
    this.audioTracks_ = options.audioTracks;

    // Manually track progress in cases where the browser/flash player doesn't report it.
    if (!this.featuresProgressEvents) {
      this.manualProgressOn();
    }

    // Manually track timeupdates in cases where the browser/flash player doesn't report it.
    if (!this.featuresTimeupdateEvents) {
      this.manualTimeUpdatesOn();
    }

    if (options.nativeCaptions === false || options.nativeTextTracks === false) {
      this.featuresNativeTextTracks = false;
    }

    if (!this.featuresNativeTextTracks) {
      this.on('ready', this.emulateTextTracks);
    }

    this.initTextTrackListeners();
    this.initTrackListeners();

    // Turn on component tap events
    this.emitTapEvents();
  }

  /**
   * List of associated text tracks
   *
   * @type {TextTrackList}
   * @private
   */

  /* Fallbacks for unsupported event types
  ================================================================================ */
  // Manually trigger progress events based on changes to the buffered amount
  // Many flash players and older HTML5 browsers don't send progress or progress-like events
  /**
   * Turn on progress events
   *
   * @method manualProgressOn
   */

  Tech.prototype.manualProgressOn = function manualProgressOn() {
    this.on('durationchange', this.onDurationChange);

    this.manualProgress = true;

    // Trigger progress watching when a source begins loading
    this.one('ready', this.trackProgress);
  };

  /**
   * Turn off progress events
   *
   * @method manualProgressOff
   */

  Tech.prototype.manualProgressOff = function manualProgressOff() {
    this.manualProgress = false;
    this.stopTrackingProgress();

    this.off('durationchange', this.onDurationChange);
  };

  /**
   * Track progress
   *
   * @method trackProgress
   */

  Tech.prototype.trackProgress = function trackProgress() {
    this.stopTrackingProgress();
    this.progressInterval = this.setInterval(Fn.bind(this, function () {
      // Don't trigger unless buffered amount is greater than last time

      var numBufferedPercent = this.bufferedPercent();

      if (this.bufferedPercent_ !== numBufferedPercent) {
        this.trigger('progress');
      }

      this.bufferedPercent_ = numBufferedPercent;

      if (numBufferedPercent === 1) {
        this.stopTrackingProgress();
      }
    }), 500);
  };

  /**
   * Update duration
   *
   * @method onDurationChange
   */

  Tech.prototype.onDurationChange = function onDurationChange() {
    this.duration_ = this.duration();
  };

  /**
   * Create and get TimeRange object for buffering
   *
   * @return {TimeRangeObject}
   * @method buffered
   */

  Tech.prototype.buffered = function buffered() {
    return _utilsTimeRangesJs.createTimeRange(0, 0);
  };

  /**
   * Get buffered percent
   *
   * @return {Number}
   * @method bufferedPercent
   */

  Tech.prototype.bufferedPercent = function bufferedPercent() {
    return _utilsBufferJs.bufferedPercent(this.buffered(), this.duration_);
  };

  /**
   * Stops tracking progress by clearing progress interval
   *
   * @method stopTrackingProgress
   */

  Tech.prototype.stopTrackingProgress = function stopTrackingProgress() {
    this.clearInterval(this.progressInterval);
  };

  /*! Time Tracking -------------------------------------------------------------- */
  /**
   * Set event listeners for on play and pause and tracking current time
   *
   * @method manualTimeUpdatesOn
   */

  Tech.prototype.manualTimeUpdatesOn = function manualTimeUpdatesOn() {
    this.manualTimeUpdates = true;

    this.on('play', this.trackCurrentTime);
    this.on('pause', this.stopTrackingCurrentTime);
  };

  /**
   * Remove event listeners for on play and pause and tracking current time
   *
   * @method manualTimeUpdatesOff
   */

  Tech.prototype.manualTimeUpdatesOff = function manualTimeUpdatesOff() {
    this.manualTimeUpdates = false;
    this.stopTrackingCurrentTime();
    this.off('play', this.trackCurrentTime);
    this.off('pause', this.stopTrackingCurrentTime);
  };

  /**
   * Tracks current time
   *
   * @method trackCurrentTime
   */

  Tech.prototype.trackCurrentTime = function trackCurrentTime() {
    if (this.currentTimeInterval) {
      this.stopTrackingCurrentTime();
    }
    this.currentTimeInterval = this.setInterval(function () {
      this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
    }, 250); // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
  };

  /**
   * Turn off play progress tracking (when paused or dragging)
   *
   * @method stopTrackingCurrentTime
   */

  Tech.prototype.stopTrackingCurrentTime = function stopTrackingCurrentTime() {
    this.clearInterval(this.currentTimeInterval);

    // #1002 - if the video ends right before the next timeupdate would happen,
    // the progress bar won't make it all the way to the end
    this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
  };

  /**
   * Turn off any manual progress or timeupdate tracking
   *
   * @method dispose
   */

  Tech.prototype.dispose = function dispose() {

    // clear out all tracks because we can't reuse them between techs
    this.clearTracks(['audio', 'video', 'text']);

    // Turn off any manual progress or timeupdate tracking
    if (this.manualProgress) {
      this.manualProgressOff();
    }

    if (this.manualTimeUpdates) {
      this.manualTimeUpdatesOff();
    }

    _Component.prototype.dispose.call(this);
  };

  /**
   * clear out a track list, or multiple track lists
   *
   * Note: Techs without source handlers should call this between
   * sources for video & audio tracks, as usually you don't want
   * to use them between tracks and we have no automatic way to do
   * it for you
   *
   * @method clearTracks
   * @param {Array|String} types type(s) of track lists to empty
   */

  Tech.prototype.clearTracks = function clearTracks(types) {
    var _this = this;

    types = [].concat(types);
    // clear out all tracks because we can't reuse them between techs
    types.forEach(function (type) {
      var list = _this[type + 'Tracks']() || [];
      var i = list.length;
      while (i--) {
        var track = list[i];
        if (type === 'text') {
          _this.removeRemoteTextTrack(track);
        }
        list.removeTrack_(track);
      }
    });
  };

  /**
   * Reset the tech. Removes all sources and resets readyState.
   *
   * @method reset
   */

  Tech.prototype.reset = function reset() {};

  /**
   * When invoked without an argument, returns a MediaError object
   * representing the current error state of the player or null if
   * there is no error. When invoked with an argument, set the current
   * error state of the player.
   * @param {MediaError=} err    Optional an error object
   * @return {MediaError}        the current error object or null
   * @method error
   */

  Tech.prototype.error = function error(err) {
    if (err !== undefined) {
      if (err instanceof _mediaErrorJs2['default']) {
        this.error_ = err;
      } else {
        this.error_ = new _mediaErrorJs2['default'](err);
      }
      this.trigger('error');
    }
    return this.error_;
  };

  /**
   * Return the time ranges that have been played through for the
   * current source. This implementation is incomplete. It does not
   * track the played time ranges, only whether the source has played
   * at all or not.
   * @return {TimeRangeObject} a single time range if this video has
   * played or an empty set of ranges if not.
   * @method played
   */

  Tech.prototype.played = function played() {
    if (this.hasStarted_) {
      return _utilsTimeRangesJs.createTimeRange(0, 0);
    }
    return _utilsTimeRangesJs.createTimeRange();
  };

  /**
   * Set current time
   *
   * @method setCurrentTime
   */

  Tech.prototype.setCurrentTime = function setCurrentTime() {
    // improve the accuracy of manual timeupdates
    if (this.manualTimeUpdates) {
      this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
    }
  };

  /**
   * Initialize texttrack listeners
   *
   * @method initTextTrackListeners
   */

  Tech.prototype.initTextTrackListeners = function initTextTrackListeners() {
    var textTrackListChanges = Fn.bind(this, function () {
      this.trigger('texttrackchange');
    });

    var tracks = this.textTracks();

    if (!tracks) return;

    tracks.addEventListener('removetrack', textTrackListChanges);
    tracks.addEventListener('addtrack', textTrackListChanges);

    this.on('dispose', Fn.bind(this, function () {
      tracks.removeEventListener('removetrack', textTrackListChanges);
      tracks.removeEventListener('addtrack', textTrackListChanges);
    }));
  };

  /**
   * Initialize audio and video track listeners
   *
   * @method initTrackListeners
   */

  Tech.prototype.initTrackListeners = function initTrackListeners() {
    var _this2 = this;

    var trackTypes = ['video', 'audio'];

    trackTypes.forEach(function (type) {
      var trackListChanges = function trackListChanges() {
        _this2.trigger(type + 'trackchange');
      };

      var tracks = _this2[type + 'Tracks']();

      tracks.addEventListener('removetrack', trackListChanges);
      tracks.addEventListener('addtrack', trackListChanges);

      _this2.on('dispose', function () {
        tracks.removeEventListener('removetrack', trackListChanges);
        tracks.removeEventListener('addtrack', trackListChanges);
      });
    });
  };

  /**
   * Emulate texttracks
   *
   * @method emulateTextTracks
   */

  Tech.prototype.emulateTextTracks = function emulateTextTracks() {
    var _this3 = this;

    var tracks = this.textTracks();
    if (!tracks) {
      return;
    }

    if (!_globalWindow2['default']['WebVTT'] && this.el().parentNode != null) {
      (function () {
        var script = _globalDocument2['default'].createElement('script');
        script.src = _this3.options_['vtt.js'] || 'https://cdn.rawgit.com/gkatsev/vtt.js/vjs-v0.12.1/dist/vtt.min.js';
        script.onload = function () {
          _this3.trigger('vttjsloaded');
        };
        script.onerror = function () {
          _this3.trigger('vttjserror');
        };
        _this3.on('dispose', function () {
          script.onload = null;
          script.onerror = null;
        });
        // but have not loaded yet and we set it to true before the inject so that
        // we don't overwrite the injected window.WebVTT if it loads right away
        _globalWindow2['default']['WebVTT'] = true;
        _this3.el().parentNode.appendChild(script);
      })();
    }

    var updateDisplay = function updateDisplay() {
      return _this3.trigger('texttrackchange');
    };
    var textTracksChanges = function textTracksChanges() {
      updateDisplay();

      for (var i = 0; i < tracks.length; i++) {
        var track = tracks[i];
        track.removeEventListener('cuechange', updateDisplay);
        if (track.mode === 'showing') {
          track.addEventListener('cuechange', updateDisplay);
        }
      }
    };

    textTracksChanges();
    tracks.addEventListener('change', textTracksChanges);

    this.on('dispose', function () {
      tracks.removeEventListener('change', textTracksChanges);
    });
  };

  /**
   * Get videotracks
   *
   * @returns {VideoTrackList}
   * @method videoTracks
   */

  Tech.prototype.videoTracks = function videoTracks() {
    this.videoTracks_ = this.videoTracks_ || new _tracksVideoTrackList2['default']();
    return this.videoTracks_;
  };

  /**
   * Get audiotracklist
   *
   * @returns {AudioTrackList}
   * @method audioTracks
   */

  Tech.prototype.audioTracks = function audioTracks() {
    this.audioTracks_ = this.audioTracks_ || new _tracksAudioTrackList2['default']();
    return this.audioTracks_;
  };

  /*
   * Provide default methods for text tracks.
   *
   * Html5 tech overrides these.
   */

  /**
   * Get texttracks
   *
   * @returns {TextTrackList}
   * @method textTracks
   */

  Tech.prototype.textTracks = function textTracks() {
    this.textTracks_ = this.textTracks_ || new _tracksTextTrackList2['default']();
    return this.textTracks_;
  };

  /**
   * Get remote texttracks
   *
   * @returns {TextTrackList}
   * @method remoteTextTracks
   */

  Tech.prototype.remoteTextTracks = function remoteTextTracks() {
    this.remoteTextTracks_ = this.remoteTextTracks_ || new _tracksTextTrackList2['default']();
    return this.remoteTextTracks_;
  };

  /**
   * Get remote htmltrackelements
   *
   * @returns {HTMLTrackElementList}
   * @method remoteTextTrackEls
   */

  Tech.prototype.remoteTextTrackEls = function remoteTextTrackEls() {
    this.remoteTextTrackEls_ = this.remoteTextTrackEls_ || new _tracksHtmlTrackElementList2['default']();
    return this.remoteTextTrackEls_;
  };

  /**
   * Creates and returns a remote text track object
   *
   * @param {String} kind Text track kind (subtitles, captions, descriptions
   *                                       chapters and metadata)
   * @param {String=} label Label to identify the text track
   * @param {String=} language Two letter language abbreviation
   * @return {TextTrackObject}
   * @method addTextTrack
   */

  Tech.prototype.addTextTrack = function addTextTrack(kind, label, language) {
    if (!kind) {
      throw new Error('TextTrack kind is required but was not provided');
    }

    return createTrackHelper(this, kind, label, language);
  };

  /**
   * Creates a remote text track object and returns a emulated html track element
   *
   * @param {Object} options The object should contain values for
   * kind, language, label and src (location of the WebVTT file)
   * @return {HTMLTrackElement}
   * @method addRemoteTextTrack
   */

  Tech.prototype.addRemoteTextTrack = function addRemoteTextTrack(options) {
    var track = _utilsMergeOptionsJs2['default'](options, {
      tech: this
    });

    var htmlTrackElement = new _tracksHtmlTrackElement2['default'](track);

    // store HTMLTrackElement and TextTrack to remote list
    this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
    this.remoteTextTracks().addTrack_(htmlTrackElement.track);

    // must come after remoteTextTracks()
    this.textTracks().addTrack_(htmlTrackElement.track);

    return htmlTrackElement;
  };

  /**
   * Remove remote texttrack
   *
   * @param {TextTrackObject} track Texttrack to remove
   * @method removeRemoteTextTrack
   */

  Tech.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
    this.textTracks().removeTrack_(track);

    var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track);

    // remove HTMLTrackElement and TextTrack from remote list
    this.remoteTextTrackEls().removeTrackElement_(trackElement);
    this.remoteTextTracks().removeTrack_(track);
  };

  /**
   * Provide a default setPoster method for techs
   * Poster support for techs should be optional, so we don't want techs to
   * break if they don't have a way to set a poster.
   *
   * @method setPoster
   */

  Tech.prototype.setPoster = function setPoster() {};

  /**
   * A method to check for the presence of the 'playsinine' <video> attribute.
   *
   * @abstract
   */

  Tech.prototype.playsinline = function playsinline() {};

  /**
   * A method to set or unset the 'playsinine' <video> attribute.
   *
   * @abstract
   */

  Tech.prototype.setPlaysinline = function setPlaysinline() {};

  /*
   * Check if the tech can support the given type
   *
   * The base tech does not support any type, but source handlers might
   * overwrite this.
   *
   * @param  {String} type    The mimetype to check
   * @return {String}         'probably', 'maybe', or '' (empty string)
   */

  Tech.prototype.canPlayType = function canPlayType() {
    return '';
  };

  /*
   * Return whether the argument is a Tech or not.
   * Can be passed either a Class like `Html5` or a instance like `player.tech_`
   *
   * @param {Object} component An item to check
   * @return {Boolean}         Whether it is a tech or not
   */

  Tech.isTech = function isTech(component) {
    return component.prototype instanceof Tech || component instanceof Tech || component === Tech;
  };

  /**
   * Registers a Tech
   *
   * @param {String} name Name of the Tech to register
   * @param {Object} tech The tech to register
   * @static
   * @method registerComponent
   */

  Tech.registerTech = function registerTech(name, tech) {
    if (!Tech.techs_) {
      Tech.techs_ = {};
    }

    if (!Tech.isTech(tech)) {
      throw new Error('Tech ' + name + ' must be a Tech');
    }

    Tech.techs_[name] = tech;
    return tech;
  };

  /**
   * Gets a component by name
   *
   * @param {String} name Name of the component to get
   * @return {Component}
   * @static
   * @method getComponent
   */

  Tech.getTech = function getTech(name) {
    if (Tech.techs_ && Tech.techs_[name]) {
      return Tech.techs_[name];
    }

    if (_globalWindow2['default'] && _globalWindow2['default'].videojs && _globalWindow2['default'].videojs[name]) {
      _utilsLogJs2['default'].warn('The ' + name + ' tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)');
      return _globalWindow2['default'].videojs[name];
    }
  };

  return Tech;
})(_component2['default']);

Tech.prototype.textTracks_;

/**
 * List of associated audio tracks
 *
 * @type {AudioTrackList}
 * @private
 */
Tech.prototype.audioTracks_;

/**
 * List of associated video tracks
 *
 * @type {VideoTrackList}
 * @private
 */
Tech.prototype.videoTracks_;

var createTrackHelper = function createTrackHelper(self, kind, label, language) {
  var options = arguments.length <= 4 || arguments[4] === undefined ? {} : arguments[4];

  var tracks = self.textTracks();

  options.kind = kind;

  if (label) {
    options.label = label;
  }
  if (language) {
    options.language = language;
  }
  options.tech = self;

  var track = new _tracksTextTrack2['default'](options);
  tracks.addTrack_(track);

  return track;
};

Tech.prototype.featuresVolumeControl = true;
Tech.prototype.featuresMutedControl = true;

// Resizing plugins using request fullscreen reloads the plugin
Tech.prototype.featuresFullscreenResize = false;
Tech.prototype.featuresPlaybackRate = false;

// Optional events that we can manually mimic with timers
// currently not triggered by video-js-swf
Tech.prototype.featuresProgressEvents = false;
Tech.prototype.featuresTimeupdateEvents = false;

Tech.prototype.featuresNativeTextTracks = false;

/*
 * A functional mixin for techs that want to use the Source Handler pattern.
 *
 * ##### EXAMPLE:
 *
 *   Tech.withSourceHandlers.call(MyTech);
 *
 */
Tech.withSourceHandlers = function (_Tech) {
  /*
   * Register a source handler
   * Source handlers are scripts for handling specific formats.
   * The source handler pattern is used for adaptive formats (HLS, DASH) that
   * manually load video data and feed it into a Source Buffer (Media Source Extensions)
   * @param  {Function} handler  The source handler
   * @param  {Boolean}  first    Register it before any existing handlers
   */
  _Tech.registerSourceHandler = function (handler, index) {
    var handlers = _Tech.sourceHandlers;

    if (!handlers) {
      handlers = _Tech.sourceHandlers = [];
    }

    if (index === undefined) {
      // add to the end of the list
      index = handlers.length;
    }

    handlers.splice(index, 0, handler);
  };

  /*
   * Check if the tech can support the given type
   * @param  {String} type    The mimetype to check
   * @return {String}         'probably', 'maybe', or '' (empty string)
   */
  _Tech.canPlayType = function (type) {
    var handlers = _Tech.sourceHandlers || [];
    var can = undefined;

    for (var i = 0; i < handlers.length; i++) {
      can = handlers[i].canPlayType(type);

      if (can) {
        return can;
      }
    }

    return '';
  };

  /*
   * Return the first source handler that supports the source
   * TODO: Answer question: should 'probably' be prioritized over 'maybe'
   * @param  {Object} source The source object
   * @returns {Object}       The first source handler that supports the source
   * @returns {null}         Null if no source handler is found
   */
  _Tech.selectSourceHandler = function (source) {
    var handlers = _Tech.sourceHandlers || [];
    var can = undefined;

    for (var i = 0; i < handlers.length; i++) {
      can = handlers[i].canHandleSource(source);

      if (can) {
        return handlers[i];
      }
    }

    return null;
  };

  /*
   * Check if the tech can support the given source
   * @param  {Object} srcObj  The source object
   * @return {String}         'probably', 'maybe', or '' (empty string)
   */
  _Tech.canPlaySource = function (srcObj) {
    var sh = _Tech.selectSourceHandler(srcObj);

    if (sh) {
      return sh.canHandleSource(srcObj);
    }

    return '';
  };

  /*
   * When using a source handler, prefer its implementation of
   * any function normally provided by the tech.
   */
  var deferrable = ['seekable', 'duration'];

  deferrable.forEach(function (fnName) {
    var originalFn = this[fnName];

    if (typeof originalFn !== 'function') {
      return;
    }

    this[fnName] = function () {
      if (this.sourceHandler_ && this.sourceHandler_[fnName]) {
        return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments);
      }
      return originalFn.apply(this, arguments);
    };
  }, _Tech.prototype);

  /*
   * Create a function for setting the source using a source object
   * and source handlers.
   * Should never be called unless a source handler was found.
   * @param {Object} source  A source object with src and type keys
   * @return {Tech} self
   */
  _Tech.prototype.setSource = function (source) {
    var sh = _Tech.selectSourceHandler(source);

    if (!sh) {
      // Fall back to a native source hander when unsupported sources are
      // deliberately set
      if (_Tech.nativeSourceHandler) {
        sh = _Tech.nativeSourceHandler;
      } else {
        _utilsLogJs2['default'].error('No source hander found for the current source.');
      }
    }

    // Dispose any existing source handler
    this.disposeSourceHandler();
    this.off('dispose', this.disposeSourceHandler);

    // if we have a source and get another one
    // then we are loading something new
    // than clear all of our current tracks
    if (this.currentSource_) {
      this.clearTracks(['audio', 'video']);

      this.currentSource_ = null;
    }

    if (sh !== _Tech.nativeSourceHandler) {

      this.currentSource_ = source;

      // Catch if someone replaced the src without calling setSource.
      // If they do, set currentSource_ to null and dispose our source handler.
      this.off(this.el_, 'loadstart', _Tech.prototype.firstLoadStartListener_);
      this.off(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_);
      this.one(this.el_, 'loadstart', _Tech.prototype.firstLoadStartListener_);
    }

    this.sourceHandler_ = sh.handleSource(source, this, this.options_);
    this.on('dispose', this.disposeSourceHandler);

    return this;
  };

  // On the first loadstart after setSource
  _Tech.prototype.firstLoadStartListener_ = function () {
    this.one(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_);
  };

  // On successive loadstarts when setSource has not been called again
  _Tech.prototype.successiveLoadStartListener_ = function () {
    this.currentSource_ = null;
    this.disposeSourceHandler();
    this.one(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_);
  };

  /*
   * Clean up any existing source handler
   */
  _Tech.prototype.disposeSourceHandler = function () {
    if (this.sourceHandler_ && this.sourceHandler_.dispose) {
      this.off(this.el_, 'loadstart', _Tech.prototype.firstLoadStartListener_);
      this.off(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_);
      this.sourceHandler_.dispose();
    }
  };
};

_component2['default'].registerComponent('Tech', Tech);
// Old name for Tech
_component2['default'].registerComponent('MediaTechController', Tech);
Tech.registerTech('Tech', Tech);
exports['default'] = Tech;
module.exports = exports['default'];

},{"../component":66,"../media-error.js":108,"../tracks/audio-track":127,"../tracks/audio-track-list":126,"../tracks/html-track-element":129,"../tracks/html-track-element-list":128,"../tracks/text-track":135,"../tracks/text-track-list":133,"../tracks/video-track":140,"../tracks/video-track-list":139,"../utils/buffer.js":142,"../utils/fn.js":146,"../utils/log.js":149,"../utils/merge-options.js":150,"../utils/time-ranges.js":152,"global/document":7,"global/window":8}],125:[function(_dereq_,module,exports){
/**
 * @file tooltip.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _component = _dereq_('./component');

var _component2 = _interopRequireDefault(_component);

var _utilsDomJs = _dereq_('./utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsEventsJs = _dereq_('./utils/events.js');

var Events = _interopRequireWildcard(_utilsEventsJs);

var _utilsFnJs = _dereq_('./utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _lodashCompatFunctionThrottle = _dereq_('lodash-compat/function/throttle');

var _lodashCompatFunctionThrottle2 = _interopRequireDefault(_lodashCompatFunctionThrottle);

/**
 * Base class for all tooltips
 *
 * @param {Object} player  Main Player
 * @param {Object=} options Object of option names and values
 * @extends Component
 * @class Tooltip
 */

var Tooltip = (function (_Component) {
  _inherits(Tooltip, _Component);

  function Tooltip(player, options) {
    _classCallCheck(this, Tooltip);

    _Component.call(this, player, options);

    this.handleHover_ = _lodashCompatFunctionThrottle2['default'](Fn.bind(this, this.handleHover), 25);
    this.handleLeave_ = _lodashCompatFunctionThrottle2['default'](Fn.bind(this, this.handleLeave), 25);
  }

  Tooltip.prototype.createEl = function createEl() {
    return _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-tooltip'
    });
  };

  Tooltip.prototype.text = function text(_text) {
    if (!_text) return this.text_ || '';

    this.text_ = _text;
    this.el_.innerHTML = this.localize(this.text_);

    this.update();

    return this;
  };

  Tooltip.prototype.handler = function handler(_handler) {
    if (!_handler) return this.handler;

    if (this.handler_) {
      this.off(this.handler_, 'mouseover', this.handleHover_);
      this.off(this.handler_, 'mouseout', this.handleLeave_);
    }

    this.handler_ = _handler;
    this.on(this.handler_, 'mouseover', this.handleHover_);
    this.on(this.handler_, 'mouseout', this.handleLeave_);

    return this;
  };

  Tooltip.prototype.handleHover = function handleHover() {
    var _this = this;

    if (this.timeout) {
      this.clearTimeout(this.timeout);
      this.timeout = 0;
    }
    this.timeout = this.setTimeout(function () {
      return _this.hide();
    }, 1000);
    this.show();
  };

  Tooltip.prototype.handleLeave = function handleLeave(event) {
    var _this2 = this;

    if (this.timeout) {
      this.clearTimeout(this.timeout);
      this.timeout = 0;
    }
    this.timeout = this.setTimeout(function () {
      return _this2.hide();
    }, 50);
  };

  Tooltip.prototype.show = function show() {
    var _this3 = this;

    this.player_.trigger('tooltipShown');
    this.player_.on('tooltipShown', function () {
      return _this3.hide();
    });

    this.addClass('vjs-tooltip-active');
    this.update();
  };

  Tooltip.prototype.hide = function hide() {
    this.removeClass('vjs-tooltip-active');

    this.player_.off('tooltipShown');
  };

  Tooltip.prototype.update = function update() {
    var width = this.width();

    if (!width) {
      return this;
    }

    var parent_ = this.handler_;
    var parentWidth = parent_ ? parent_.offsetWidth : 0;

    var parentLeft = Dom.findElPosition(parent_).left - Dom.findElPosition(this.player_.el()).left;
    var skew = parentWidth / 2 - width / 2;

    var minSkew = -parentLeft;
    // TODO: player_.width() is not working properly after switch to fullscreen
    var maxSkew = this.player_.el().offsetWidth - parentLeft - width;

    skew = Math.min(Math.max(minSkew, skew), maxSkew);

    this.el_.style.left = skew + 'px';

    return this;
  };

  return Tooltip;
})(_component2['default']);

_component2['default'].registerComponent('Tooltip', Tooltip);
exports['default'] = Tooltip;
module.exports = exports['default'];

},{"./component":66,"./utils/dom.js":144,"./utils/events.js":145,"./utils/fn.js":146,"global/document":7,"lodash-compat/function/throttle":13}],126:[function(_dereq_,module,exports){
/**
 * @file audio-track-list.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _trackList = _dereq_('./track-list');

var _trackList2 = _interopRequireDefault(_trackList);

var _utilsBrowserJs = _dereq_('../utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

/**
 * anywhere we call this function we diverge from the spec
 * as we only support one enabled audiotrack at a time
 *
 * @param {Array|AudioTrackList} list list to work on
 * @param {AudioTrack} track the track to skip
 */
var disableOthers = function disableOthers(list, track) {
  for (var i = 0; i < list.length; i++) {
    if (track.id === list[i].id) {
      continue;
    }
    // another audio track is enabled, disable it
    list[i].enabled = false;
  }
};
/**
 * A list of possible audio tracks. All functionality is in the
 * base class Tracklist and the spec for AudioTrackList is located at:
 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
 *
 * interface AudioTrackList : EventTarget {
 *   readonly attribute unsigned long length;
 *   getter AudioTrack (unsigned long index);
 *   AudioTrack? getTrackById(DOMString id);
 *
 *   attribute EventHandler onchange;
 *   attribute EventHandler onaddtrack;
 *   attribute EventHandler onremovetrack;
 * };
 *
 * @param {AudioTrack[]} tracks a list of audio tracks to instantiate the list with
 * @extends TrackList
 * @class AudioTrackList
 */

var AudioTrackList = (function (_TrackList) {
  _inherits(AudioTrackList, _TrackList);

  function AudioTrackList() {
    var tracks = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];

    _classCallCheck(this, AudioTrackList);

    var list = undefined;

    // make sure only 1 track is enabled
    // sorted from last index to first index
    for (var i = tracks.length - 1; i >= 0; i--) {
      if (tracks[i].enabled) {
        disableOthers(tracks, tracks[i]);
        break;
      }
    }

    // IE8 forces us to implement inheritance ourselves
    // as it does not support Object.defineProperty properly
    if (browser.IS_IE8) {
      list = _globalDocument2['default'].createElement('custom');
      for (var prop in _trackList2['default'].prototype) {
        if (prop !== 'constructor') {
          list[prop] = _trackList2['default'].prototype[prop];
        }
      }
      for (var prop in AudioTrackList.prototype) {
        if (prop !== 'constructor') {
          list[prop] = AudioTrackList.prototype[prop];
        }
      }
    }

    list = _TrackList.call(this, tracks, list);
    list.changing_ = false;

    return list;
  }

  AudioTrackList.prototype.addTrack_ = function addTrack_(track) {
    var _this = this;

    if (track.enabled) {
      disableOthers(this, track);
    }

    _TrackList.prototype.addTrack_.call(this, track);
    // native tracks don't have this
    if (!track.addEventListener) {
      return;
    }

    track.addEventListener('enabledchange', function () {
      // when we are disabling other tracks (since we don't support
      // more than one track at a time) we will set changing_
      // to true so that we don't trigger additional change events
      if (_this.changing_) {
        return;
      }
      _this.changing_ = true;
      disableOthers(_this, track);
      _this.changing_ = false;
      _this.trigger('change');
    });
  };

  AudioTrackList.prototype.addTrack = function addTrack(track) {
    this.addTrack_(track);
  };

  AudioTrackList.prototype.removeTrack = function removeTrack(track) {
    _TrackList.prototype.removeTrack_.call(this, track);
  };

  return AudioTrackList;
})(_trackList2['default']);

exports['default'] = AudioTrackList;
module.exports = exports['default'];

},{"../utils/browser.js":141,"./track-list":137,"global/document":7}],127:[function(_dereq_,module,exports){
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _trackEnums = _dereq_('./track-enums');

var _track = _dereq_('./track');

var _track2 = _interopRequireDefault(_track);

var _utilsMergeOptions = _dereq_('../utils/merge-options');

var _utilsMergeOptions2 = _interopRequireDefault(_utilsMergeOptions);

var _utilsBrowserJs = _dereq_('../utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

/**
 * A single audio text track as defined in:
 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack
 *
 * interface AudioTrack {
 *   readonly attribute DOMString id;
 *   readonly attribute DOMString kind;
 *   readonly attribute DOMString label;
 *   readonly attribute DOMString language;
 *   attribute boolean enabled;
 * };
 *
 * @param {Object=} options Object of option names and values
 * @class AudioTrack
 */

var AudioTrack = (function (_Track) {
  _inherits(AudioTrack, _Track);

  function AudioTrack() {
    var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

    _classCallCheck(this, AudioTrack);

    var settings = _utilsMergeOptions2['default'](options, {
      kind: _trackEnums.AudioTrackKind[options.kind] || ''
    });
    // on IE8 this will be a document element
    // for every other browser this will be a normal object
    var track = _Track.call(this, settings);
    var enabled = false;

    if (browser.IS_IE8) {
      for (var prop in AudioTrack.prototype) {
        if (prop !== 'constructor') {
          track[prop] = AudioTrack.prototype[prop];
        }
      }
    }

    Object.defineProperty(track, 'enabled', {
      get: function get() {
        return enabled;
      },
      set: function set(newEnabled) {
        // an invalid or unchanged value
        if (typeof newEnabled !== 'boolean' || newEnabled === enabled) {
          return;
        }
        enabled = newEnabled;
        this.trigger('enabledchange');
      }
    });

    // if the user sets this track to selected then
    // set selected to that true value otherwise
    // we keep it false
    if (settings.enabled) {
      track.enabled = settings.enabled;
    }
    track.loaded_ = true;

    return track;
  }

  return AudioTrack;
})(_track2['default']);

exports['default'] = AudioTrack;
module.exports = exports['default'];

},{"../utils/browser.js":141,"../utils/merge-options":150,"./track":138,"./track-enums":136}],128:[function(_dereq_,module,exports){
/**
 * @file html-track-element-list.js
 */

'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _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; } }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var _utilsBrowserJs = _dereq_('../utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var HtmlTrackElementList = (function () {
  function HtmlTrackElementList() {
    var trackElements = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];

    _classCallCheck(this, HtmlTrackElementList);

    var list = this;

    if (browser.IS_IE8) {
      list = _globalDocument2['default'].createElement('custom');

      for (var prop in HtmlTrackElementList.prototype) {
        if (prop !== 'constructor') {
          list[prop] = HtmlTrackElementList.prototype[prop];
        }
      }
    }

    list.trackElements_ = [];

    Object.defineProperty(list, 'length', {
      get: function get() {
        return this.trackElements_.length;
      }
    });

    for (var i = 0, _length = trackElements.length; i < _length; i++) {
      list.addTrackElement_(trackElements[i]);
    }

    if (browser.IS_IE8) {
      return list;
    }
  }

  HtmlTrackElementList.prototype.addTrackElement_ = function addTrackElement_(trackElement) {
    this.trackElements_.push(trackElement);
  };

  HtmlTrackElementList.prototype.getTrackElementByTrack_ = function getTrackElementByTrack_(track) {
    var trackElement_ = undefined;

    for (var i = 0, _length2 = this.trackElements_.length; i < _length2; i++) {
      if (track === this.trackElements_[i].track) {
        trackElement_ = this.trackElements_[i];

        break;
      }
    }

    return trackElement_;
  };

  HtmlTrackElementList.prototype.removeTrackElement_ = function removeTrackElement_(trackElement) {
    for (var i = 0, _length3 = this.trackElements_.length; i < _length3; i++) {
      if (trackElement === this.trackElements_[i]) {
        this.trackElements_.splice(i, 1);

        break;
      }
    }
  };

  return HtmlTrackElementList;
})();

exports['default'] = HtmlTrackElementList;
module.exports = exports['default'];

},{"../utils/browser.js":141,"global/document":7}],129:[function(_dereq_,module,exports){
/**
 * @file html-track-element.js
 */

'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _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; } }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _utilsBrowserJs = _dereq_('../utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _eventTarget = _dereq_('../event-target');

var _eventTarget2 = _interopRequireDefault(_eventTarget);

var _tracksTextTrack = _dereq_('../tracks/text-track');

var _tracksTextTrack2 = _interopRequireDefault(_tracksTextTrack);

var NONE = 0;
var LOADING = 1;
var LOADED = 2;
var ERROR = 3;

/**
 * https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement
 *
 * interface HTMLTrackElement : HTMLElement {
 *   attribute DOMString kind;
 *   attribute DOMString src;
 *   attribute DOMString srclang;
 *   attribute DOMString label;
 *   attribute boolean default;
 *
 *   const unsigned short NONE = 0;
 *   const unsigned short LOADING = 1;
 *   const unsigned short LOADED = 2;
 *   const unsigned short ERROR = 3;
 *   readonly attribute unsigned short readyState;
 *
 *   readonly attribute TextTrack track;
 * };
 *
 * @param {Object} options TextTrack configuration
 * @class HTMLTrackElement
 */

var HTMLTrackElement = (function (_EventTarget) {
  _inherits(HTMLTrackElement, _EventTarget);

  function HTMLTrackElement() {
    var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

    _classCallCheck(this, HTMLTrackElement);

    _EventTarget.call(this);

    var readyState = undefined,
        trackElement = this;

    if (browser.IS_IE8) {
      trackElement = _globalDocument2['default'].createElement('custom');

      for (var prop in HTMLTrackElement.prototype) {
        if (prop !== 'constructor') {
          trackElement[prop] = HTMLTrackElement.prototype[prop];
        }
      }
    }

    var track = new _tracksTextTrack2['default'](options);

    trackElement.kind = track.kind;
    trackElement.src = track.src;
    trackElement.srclang = track.language;
    trackElement.label = track.label;
    trackElement['default'] = track['default'];

    Object.defineProperty(trackElement, 'readyState', {
      get: function get() {
        return readyState;
      }
    });

    Object.defineProperty(trackElement, 'track', {
      get: function get() {
        return track;
      }
    });

    readyState = NONE;

    track.addEventListener('loadeddata', function () {
      readyState = LOADED;

      trackElement.trigger({
        type: 'load',
        target: trackElement
      });
    });

    if (browser.IS_IE8) {
      return trackElement;
    }
  }

  return HTMLTrackElement;
})(_eventTarget2['default']);

HTMLTrackElement.prototype.allowedEvents_ = {
  load: 'load'
};

HTMLTrackElement.NONE = NONE;
HTMLTrackElement.LOADING = LOADING;
HTMLTrackElement.LOADED = LOADED;
HTMLTrackElement.ERROR = ERROR;

exports['default'] = HTMLTrackElement;
module.exports = exports['default'];

},{"../event-target":104,"../tracks/text-track":135,"../utils/browser.js":141,"global/document":7}],130:[function(_dereq_,module,exports){
/**
 * @file text-track-cue-list.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _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; } }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var _utilsBrowserJs = _dereq_('../utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

/**
 * A List of text track cues as defined in:
 * https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist
 *
 * interface TextTrackCueList {
 *   readonly attribute unsigned long length;
 *   getter TextTrackCue (unsigned long index);
 *   TextTrackCue? getCueById(DOMString id);
 * };
 *
 * @param {Array} cues A list of cues to be initialized with
 * @class TextTrackCueList
 */

var TextTrackCueList = (function () {
  function TextTrackCueList(cues) {
    _classCallCheck(this, TextTrackCueList);

    var list = this;

    if (browser.IS_IE8) {
      list = _globalDocument2['default'].createElement('custom');

      for (var prop in TextTrackCueList.prototype) {
        if (prop !== 'constructor') {
          list[prop] = TextTrackCueList.prototype[prop];
        }
      }
    }

    TextTrackCueList.prototype.setCues_.call(list, cues);

    Object.defineProperty(list, 'length', {
      get: function get() {
        return this.length_;
      }
    });

    if (browser.IS_IE8) {
      return list;
    }
  }

  /**
   * A setter for cues in this list
   *
   * @param {Array} cues an array of cues
   * @method setCues_
   * @private
   */

  TextTrackCueList.prototype.setCues_ = function setCues_(cues) {
    var oldLength = this.length || 0;
    var i = 0;
    var l = cues.length;

    this.cues_ = cues;
    this.length_ = cues.length;

    var defineProp = function defineProp(index) {
      if (!('' + index in this)) {
        Object.defineProperty(this, '' + index, {
          get: function get() {
            return this.cues_[index];
          }
        });
      }
    };

    if (oldLength < l) {
      i = oldLength;

      for (; i < l; i++) {
        defineProp.call(this, i);
      }
    }
  };

  /**
   * Get a cue that is currently in the Cue list by id
   *
   * @param {String} id
   * @method getCueById
   * @return {Object} a single cue
   */

  TextTrackCueList.prototype.getCueById = function getCueById(id) {
    var result = null;

    for (var i = 0, l = this.length; i < l; i++) {
      var cue = this[i];

      if (cue.id === id) {
        result = cue;
        break;
      }
    }

    return result;
  };

  return TextTrackCueList;
})();

exports['default'] = TextTrackCueList;
module.exports = exports['default'];

},{"../utils/browser.js":141,"global/document":7}],131:[function(_dereq_,module,exports){
/**
 * @file text-track-display.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _component = _dereq_('../component');

var _component2 = _interopRequireDefault(_component);

var _menuMenuJs = _dereq_('../menu/menu.js');

var _menuMenuJs2 = _interopRequireDefault(_menuMenuJs);

var _menuMenuItemJs = _dereq_('../menu/menu-item.js');

var _menuMenuItemJs2 = _interopRequireDefault(_menuMenuItemJs);

var _menuMenuButtonJs = _dereq_('../menu/menu-button.js');

var _menuMenuButtonJs2 = _interopRequireDefault(_menuMenuButtonJs);

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var darkGray = '#222';
var lightGray = '#ccc';
var fontMap = {
  monospace: 'monospace',
  sansSerif: 'sans-serif',
  serif: 'serif',
  monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
  monospaceSerif: '"Courier New", monospace',
  proportionalSansSerif: 'sans-serif',
  proportionalSerif: 'serif',
  casual: '"Comic Sans MS", Impact, fantasy',
  script: '"Monotype Corsiva", cursive',
  smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
};

/**
 * The component for displaying text track cues
 *
 * @param {Object} player  Main Player
 * @param {Object=} options Object of option names and values
 * @param {Function=} ready    Ready callback function
 * @extends Component
 * @class TextTrackDisplay
 */

var TextTrackDisplay = (function (_Component) {
  _inherits(TextTrackDisplay, _Component);

  function TextTrackDisplay(player, options, ready) {
    _classCallCheck(this, TextTrackDisplay);

    _Component.call(this, player, options, ready);

    player.on('loadstart', Fn.bind(this, this.toggleDisplay));
    player.on('texttrackchange', Fn.bind(this, this.updateDisplay));

    // This used to be called during player init, but was causing an error
    // if a track should show by default and the display hadn't loaded yet.
    // Should probably be moved to an external track loader when we support
    // tracks that don't need a display.
    player.ready(Fn.bind(this, function () {
      if (player.tech_ && player.tech_['featuresNativeTextTracks']) {
        this.hide();
        return;
      }

      player.on('fullscreenchange', Fn.bind(this, this.updateDisplay));

      var tracks = this.options_.playerOptions['tracks'] || [];
      for (var i = 0; i < tracks.length; i++) {
        var track = tracks[i];
        this.player_.addRemoteTextTrack(track);
      }

      var modes = { 'captions': 1, 'subtitles': 1 };
      var trackList = this.player_.textTracks();
      var firstDesc = undefined;
      var firstCaptions = undefined;

      if (trackList) {
        for (var i = 0; i < trackList.length; i++) {
          var track = trackList[i];
          if (track['default']) {
            if (track.kind === 'descriptions' && !firstDesc) {
              firstDesc = track;
            } else if (track.kind in modes && !firstCaptions) {
              firstCaptions = track;
            }
          }
        }

        // We want to show the first default track but captions and subtitles
        // take precedence over descriptions.
        // So, display the first default captions or subtitles track
        // and otherwise the first default descriptions track.
        if (firstCaptions) {
          firstCaptions.mode = 'showing';
        } else if (firstDesc) {
          firstDesc.mode = 'showing';
        }
      }
    }));
  }

  /**
  * Add cue HTML to display
  *
  * @param {Number} color Hex number for color, like #f0e
  * @param {Number} opacity Value for opacity,0.0 - 1.0
  * @return {RGBAColor} In the form 'rgba(255, 0, 0, 0.3)'
  * @method constructColor
  */

  /**
   * Toggle display texttracks
   *
   * @method toggleDisplay
   */

  TextTrackDisplay.prototype.toggleDisplay = function toggleDisplay() {
    if (this.player_.tech_ && this.player_.tech_['featuresNativeTextTracks']) {
      this.hide();
    } else {
      this.show();
    }
  };

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  TextTrackDisplay.prototype.createEl = function createEl() {
    return _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-text-track-display'
    }, {
      'aria-live': 'assertive',
      'aria-atomic': 'true'
    });
  };

  /**
   * Clear display texttracks
   *
   * @method clearDisplay
   */

  TextTrackDisplay.prototype.clearDisplay = function clearDisplay() {
    if (typeof _globalWindow2['default']['WebVTT'] === 'function') {
      _globalWindow2['default']['WebVTT']['processCues'](_globalWindow2['default'], [], this.el_);
    }
  };

  /**
   * Update display texttracks
   *
   * @method updateDisplay
   */

  TextTrackDisplay.prototype.updateDisplay = function updateDisplay() {
    var tracks = this.player_.textTracks();

    this.clearDisplay();

    if (!tracks) {
      return;
    }

    // Track display prioritization model: if multiple tracks are 'showing',
    //  display the first 'subtitles' or 'captions' track which is 'showing',
    //  otherwise display the first 'descriptions' track which is 'showing'

    var descriptionsTrack = null;
    var captionsSubtitlesTrack = null;

    var i = tracks.length;
    while (i--) {
      var track = tracks[i];
      if (track['mode'] === 'showing') {
        if (track['kind'] === 'descriptions') {
          descriptionsTrack = track;
        } else {
          captionsSubtitlesTrack = track;
        }
      }
    }

    if (captionsSubtitlesTrack) {
      this.updateForTrack(captionsSubtitlesTrack);
    } else if (descriptionsTrack) {
      this.updateForTrack(descriptionsTrack);
    }
  };

  /**
   * Add texttrack to texttrack list
   *
   * @param {TextTrackObject} track Texttrack object to be added to list
   * @method updateForTrack
   */

  TextTrackDisplay.prototype.updateForTrack = function updateForTrack(track) {
    if (typeof _globalWindow2['default']['WebVTT'] !== 'function' || !track['activeCues']) {
      return;
    }

    var overrides = this.player_['textTrackSettings'].getValues();

    var cues = [];
    for (var _i = 0; _i < track['activeCues'].length; _i++) {
      cues.push(track['activeCues'][_i]);
    }

    _globalWindow2['default']['WebVTT']['processCues'](_globalWindow2['default'], cues, this.el_);

    var i = cues.length;
    while (i--) {
      var cue = cues[i];
      if (!cue) {
        continue;
      }

      var cueDiv = cue.displayState;
      if (overrides.color) {
        cueDiv.firstChild.style.color = overrides.color;
      }
      if (overrides.textOpacity) {
        tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity));
      }
      if (overrides.backgroundColor) {
        cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
      }
      if (overrides.backgroundOpacity) {
        tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity));
      }
      if (overrides.windowColor) {
        if (overrides.windowOpacity) {
          tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity));
        } else {
          cueDiv.style.backgroundColor = overrides.windowColor;
        }
      }
      if (overrides.edgeStyle) {
        if (overrides.edgeStyle === 'dropshadow') {
          cueDiv.firstChild.style.textShadow = '2px 2px 3px ' + darkGray + ', 2px 2px 4px ' + darkGray + ', 2px 2px 5px ' + darkGray;
        } else if (overrides.edgeStyle === 'raised') {
          cueDiv.firstChild.style.textShadow = '1px 1px ' + darkGray + ', 2px 2px ' + darkGray + ', 3px 3px ' + darkGray;
        } else if (overrides.edgeStyle === 'depressed') {
          cueDiv.firstChild.style.textShadow = '1px 1px ' + lightGray + ', 0 1px ' + lightGray + ', -1px -1px ' + darkGray + ', 0 -1px ' + darkGray;
        } else if (overrides.edgeStyle === 'uniform') {
          cueDiv.firstChild.style.textShadow = '0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray;
        }
      }
      if (overrides.fontPercent && overrides.fontPercent !== 1) {
        var fontSize = cueDiv.getAttribute('data-fontsize');
        if (!fontSize) {
          fontSize = cueDiv.style.fontSize;
          cueDiv.setAttribute('data-fontsize', fontSize);
        }
        fontSize = _globalWindow2['default'].parseFloat(fontSize);
        cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px';
        cueDiv.style.height = 'auto';
        cueDiv.style.top = 'auto';
      }
      if (overrides.fontFamily && overrides.fontFamily !== 'default') {
        if (overrides.fontFamily === 'small-caps') {
          cueDiv.firstChild.style.fontVariant = 'small-caps';
        } else {
          cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
        }
      }
    }
  };

  return TextTrackDisplay;
})(_component2['default']);

function constructColor(color, opacity) {
  return 'rgba(' +
  // color looks like "#f0e"
  parseInt(color[1] + color[1], 16) + ',' + parseInt(color[2] + color[2], 16) + ',' + parseInt(color[3] + color[3], 16) + ',' + opacity + ')';
}

/**
 * Try to update style
 * Some style changes will throw an error, particularly in IE8. Those should be noops.
 *
 * @param {Element} el The element to be styles
 * @param {CSSProperty} style The CSS property to be styled
 * @param {CSSStyle} rule The actual style to be applied to the property
 * @method tryUpdateStyle
 */
function tryUpdateStyle(el, style, rule) {
  //
  try {
    el.style[style] = rule;
  } catch (e) {}
}

_component2['default'].registerComponent('TextTrackDisplay', TextTrackDisplay);
exports['default'] = TextTrackDisplay;
module.exports = exports['default'];

},{"../component":66,"../menu/menu-button.js":109,"../menu/menu-item.js":110,"../menu/menu.js":111,"../utils/fn.js":146,"global/document":7,"global/window":8}],132:[function(_dereq_,module,exports){
/**
 * Utilities for capturing text track state and re-creating tracks
 * based on a capture.
 *
 * @file text-track-list-converter.js
 */

/**
 * Examine a single text track and return a JSON-compatible javascript
 * object that represents the text track's state.
 * @param track {TextTrackObject} the text track to query
 * @return {Object} a serializable javascript representation of the
 * @private
 */
'use strict';

exports.__esModule = true;
var trackToJson_ = function trackToJson_(track) {
  var ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce(function (acc, prop, i) {
    if (track[prop]) {
      acc[prop] = track[prop];
    }

    return acc;
  }, {
    cues: track.cues && Array.prototype.map.call(track.cues, function (cue) {
      return {
        startTime: cue.startTime,
        endTime: cue.endTime,
        text: cue.text,
        id: cue.id
      };
    })
  });

  return ret;
};

/**
 * Examine a tech and return a JSON-compatible javascript array that
 * represents the state of all text tracks currently configured. The
 * return array is compatible with `jsonToTextTracks`.
 * @param tech {tech} the tech object to query
 * @return {Array} a serializable javascript representation of the
 * @function textTracksToJson
 */
var textTracksToJson = function textTracksToJson(tech) {

  var trackEls = tech.$$('track');

  var trackObjs = Array.prototype.map.call(trackEls, function (t) {
    return t.track;
  });
  var tracks = Array.prototype.map.call(trackEls, function (trackEl) {
    var json = trackToJson_(trackEl.track);
    if (trackEl.src) {
      json.src = trackEl.src;
    }
    return json;
  });

  return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) {
    return trackObjs.indexOf(track) === -1;
  }).map(trackToJson_));
};

/**
 * Creates a set of remote text tracks on a tech based on an array of
 * javascript text track representations.
 * @param json {Array} an array of text track representation objects,
 * like those that would be produced by `textTracksToJson`
 * @param tech {tech} the tech to create text tracks on
 * @function jsonToTextTracks
 */
var jsonToTextTracks = function jsonToTextTracks(json, tech) {
  json.forEach(function (track) {
    var addedTrack = tech.addRemoteTextTrack(track).track;
    if (!track.src && track.cues) {
      track.cues.forEach(function (cue) {
        return addedTrack.addCue(cue);
      });
    }
  });

  return tech.textTracks();
};

exports['default'] = { textTracksToJson: textTracksToJson, jsonToTextTracks: jsonToTextTracks, trackToJson_: trackToJson_ };
module.exports = exports['default'];

},{}],133:[function(_dereq_,module,exports){
/**
 * @file text-track-list.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _trackList = _dereq_('./track-list');

var _trackList2 = _interopRequireDefault(_trackList);

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsBrowserJs = _dereq_('../utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

/**
 * A list of possible text tracks. All functionality is in the
 * base class TrackList. The spec for TextTrackList is located at:
 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist
 *
 * interface TextTrackList : EventTarget {
 *   readonly attribute unsigned long length;
 *   getter TextTrack (unsigned long index);
 *   TextTrack? getTrackById(DOMString id);
 *
 *   attribute EventHandler onchange;
 *   attribute EventHandler onaddtrack;
 *   attribute EventHandler onremovetrack;
 * };
 *
 * @param {TextTrack[]} tracks A list of tracks to initialize the list with
 * @extends TrackList
 * @class TextTrackList
 */

var TextTrackList = (function (_TrackList) {
  _inherits(TextTrackList, _TrackList);

  function TextTrackList() {
    var tracks = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];

    _classCallCheck(this, TextTrackList);

    var list = undefined;

    // IE8 forces us to implement inheritance ourselves
    // as it does not support Object.defineProperty properly
    if (browser.IS_IE8) {
      list = _globalDocument2['default'].createElement('custom');
      for (var prop in _trackList2['default'].prototype) {
        if (prop !== 'constructor') {
          list[prop] = _trackList2['default'].prototype[prop];
        }
      }
      for (var prop in TextTrackList.prototype) {
        if (prop !== 'constructor') {
          list[prop] = TextTrackList.prototype[prop];
        }
      }
    }

    list = _TrackList.call(this, tracks, list);
    return list;
  }

  TextTrackList.prototype.addTrack_ = function addTrack_(track) {
    _TrackList.prototype.addTrack_.call(this, track);
    track.addEventListener('modechange', Fn.bind(this, function () {
      this.trigger('change');
    }));
  };

  /**
   * Remove TextTrack from TextTrackList
   * NOTE: Be mindful of what is passed in as it may be a HTMLTrackElement
   *
   * @param {TextTrack} rtrack
   * @method removeTrack_
   * @private
   */

  TextTrackList.prototype.removeTrack_ = function removeTrack_(rtrack) {
    var track = undefined;

    for (var i = 0, l = this.length; i < l; i++) {
      if (this[i] === rtrack) {
        track = this[i];
        if (track.off) {
          track.off();
        }

        this.tracks_.splice(i, 1);

        break;
      }
    }

    if (!track) {
      return;
    }

    this.trigger({
      track: track,
      type: 'removetrack'
    });
  };

  /**
   * Get a TextTrack from TextTrackList by a tracks id
   *
   * @param {String} id - the id of the track to get
   * @method getTrackById
   * @return {TextTrack}
   * @private
   */

  TextTrackList.prototype.getTrackById = function getTrackById(id) {
    var result = null;

    for (var i = 0, l = this.length; i < l; i++) {
      var track = this[i];

      if (track.id === id) {
        result = track;
        break;
      }
    }

    return result;
  };

  return TextTrackList;
})(_trackList2['default']);

exports['default'] = TextTrackList;
module.exports = exports['default'];

},{"../utils/browser.js":141,"../utils/fn.js":146,"./track-list":137,"global/document":7}],134:[function(_dereq_,module,exports){
/**
 * @file text-track-settings.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _component = _dereq_('../component');

var _component2 = _interopRequireDefault(_component);

var _utilsEventsJs = _dereq_('../utils/events.js');

var Events = _interopRequireWildcard(_utilsEventsJs);

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsLogJs = _dereq_('../utils/log.js');

var _utilsLogJs2 = _interopRequireDefault(_utilsLogJs);

var _safeJsonParseTuple = _dereq_('safe-json-parse/tuple');

var _safeJsonParseTuple2 = _interopRequireDefault(_safeJsonParseTuple);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

/**
 * Manipulate settings of texttracks
 *
 * @param {Object} player  Main Player
 * @param {Object=} options Object of option names and values
 * @extends Component
 * @class TextTrackSettings
 */

var TextTrackSettings = (function (_Component) {
  _inherits(TextTrackSettings, _Component);

  function TextTrackSettings(player, options) {
    _classCallCheck(this, TextTrackSettings);

    _Component.call(this, player, options);
    this.hide();

    // Grab `persistTextTrackSettings` from the player options if not passed in child options
    if (options.persistTextTrackSettings === undefined) {
      this.options_.persistTextTrackSettings = this.options_.playerOptions.persistTextTrackSettings;
    }

    Events.on(this.$('.vjs-done-button'), 'click', Fn.bind(this, function () {
      this.saveSettings();
      this.hide();
    }));

    Events.on(this.$('.vjs-default-button'), 'click', Fn.bind(this, function () {
      this.$('.vjs-fg-color > select').selectedIndex = 0;
      this.$('.vjs-bg-color > select').selectedIndex = 0;
      this.$('.window-color > select').selectedIndex = 0;
      this.$('.vjs-text-opacity > select').selectedIndex = 0;
      this.$('.vjs-bg-opacity > select').selectedIndex = 0;
      this.$('.vjs-window-opacity > select').selectedIndex = 0;
      this.$('.vjs-edge-style select').selectedIndex = 0;
      this.$('.vjs-font-family select').selectedIndex = 0;
      this.$('.vjs-font-percent select').selectedIndex = 2;
      this.updateDisplay();
    }));

    Events.on(this.$('.vjs-fg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
    Events.on(this.$('.vjs-bg-color > select'), 'change', Fn.bind(this, this.updateDisplay));
    Events.on(this.$('.window-color > select'), 'change', Fn.bind(this, this.updateDisplay));
    Events.on(this.$('.vjs-text-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
    Events.on(this.$('.vjs-bg-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
    Events.on(this.$('.vjs-window-opacity > select'), 'change', Fn.bind(this, this.updateDisplay));
    Events.on(this.$('.vjs-font-percent select'), 'change', Fn.bind(this, this.updateDisplay));
    Events.on(this.$('.vjs-edge-style select'), 'change', Fn.bind(this, this.updateDisplay));
    Events.on(this.$('.vjs-font-family select'), 'change', Fn.bind(this, this.updateDisplay));

    if (this.options_.persistTextTrackSettings) {
      this.restoreSettings();
    }
  }

  /**
   * Create the component's DOM element
   *
   * @return {Element}
   * @method createEl
   */

  TextTrackSettings.prototype.createEl = function createEl() {
    return _Component.prototype.createEl.call(this, 'div', {
      className: 'vjs-caption-settings vjs-modal-overlay',
      innerHTML: captionOptionsMenuTemplate()
    });
  };

  /**
   * Get texttrack settings
   * Settings are
   * .vjs-edge-style
   * .vjs-font-family
   * .vjs-fg-color
   * .vjs-text-opacity
   * .vjs-bg-color
   * .vjs-bg-opacity
   * .window-color
   * .vjs-window-opacity
   *
   * @return {Object}
   * @method getValues
   */

  TextTrackSettings.prototype.getValues = function getValues() {
    var textEdge = getSelectedOptionValue(this.$('.vjs-edge-style select'));
    var fontFamily = getSelectedOptionValue(this.$('.vjs-font-family select'));
    var fgColor = getSelectedOptionValue(this.$('.vjs-fg-color > select'));
    var textOpacity = getSelectedOptionValue(this.$('.vjs-text-opacity > select'));
    var bgColor = getSelectedOptionValue(this.$('.vjs-bg-color > select'));
    var bgOpacity = getSelectedOptionValue(this.$('.vjs-bg-opacity > select'));
    var windowColor = getSelectedOptionValue(this.$('.window-color > select'));
    var windowOpacity = getSelectedOptionValue(this.$('.vjs-window-opacity > select'));
    var fontPercent = _globalWindow2['default']['parseFloat'](getSelectedOptionValue(this.$('.vjs-font-percent > select')));

    var result = {
      'backgroundOpacity': bgOpacity,
      'textOpacity': textOpacity,
      'windowOpacity': windowOpacity,
      'edgeStyle': textEdge,
      'fontFamily': fontFamily,
      'color': fgColor,
      'backgroundColor': bgColor,
      'windowColor': windowColor,
      'fontPercent': fontPercent
    };
    for (var _name in result) {
      if (result[_name] === '' || result[_name] === 'none' || _name === 'fontPercent' && result[_name] === 1.00) {
        delete result[_name];
      }
    }
    return result;
  };

  /**
   * Set texttrack settings
   * Settings are
   * .vjs-edge-style
   * .vjs-font-family
   * .vjs-fg-color
   * .vjs-text-opacity
   * .vjs-bg-color
   * .vjs-bg-opacity
   * .window-color
   * .vjs-window-opacity
   *
   * @param {Object} values Object with texttrack setting values
   * @method setValues
   */

  TextTrackSettings.prototype.setValues = function setValues(values) {
    setSelectedOption(this.$('.vjs-edge-style select'), values.edgeStyle);
    setSelectedOption(this.$('.vjs-font-family select'), values.fontFamily);
    setSelectedOption(this.$('.vjs-fg-color > select'), values.color);
    setSelectedOption(this.$('.vjs-text-opacity > select'), values.textOpacity);
    setSelectedOption(this.$('.vjs-bg-color > select'), values.backgroundColor);
    setSelectedOption(this.$('.vjs-bg-opacity > select'), values.backgroundOpacity);
    setSelectedOption(this.$('.window-color > select'), values.windowColor);
    setSelectedOption(this.$('.vjs-window-opacity > select'), values.windowOpacity);

    var fontPercent = values.fontPercent;

    if (fontPercent) {
      fontPercent = fontPercent.toFixed(2);
    }

    setSelectedOption(this.$('.vjs-font-percent > select'), fontPercent);
  };

  /**
   * Restore texttrack settings
   *
   * @method restoreSettings
   */

  TextTrackSettings.prototype.restoreSettings = function restoreSettings() {
    var err = undefined,
        values = undefined;

    try {
      var _safeParseTuple = _safeJsonParseTuple2['default'](_globalWindow2['default'].localStorage.getItem('vjs-text-track-settings'));

      err = _safeParseTuple[0];
      values = _safeParseTuple[1];

      if (err) {
        _utilsLogJs2['default'].error(err);
      }
    } catch (e) {
      _utilsLogJs2['default'].warn(e);
    }

    if (values) {
      this.setValues(values);
    }
  };

  /**
   * Save texttrack settings to local storage
   *
   * @method saveSettings
   */

  TextTrackSettings.prototype.saveSettings = function saveSettings() {
    if (!this.options_.persistTextTrackSettings) {
      return;
    }

    var values = this.getValues();
    try {
      if (Object.getOwnPropertyNames(values).length > 0) {
        _globalWindow2['default'].localStorage.setItem('vjs-text-track-settings', JSON.stringify(values));
      } else {
        _globalWindow2['default'].localStorage.removeItem('vjs-text-track-settings');
      }
    } catch (e) {
      _utilsLogJs2['default'].warn(e);
    }
  };

  /**
   * Update display of texttrack settings
   *
   * @method updateDisplay
   */

  TextTrackSettings.prototype.updateDisplay = function updateDisplay() {
    var ttDisplay = this.player_.getChild('textTrackDisplay');
    if (ttDisplay) {
      ttDisplay.updateDisplay();
    }
  };

  return TextTrackSettings;
})(_component2['default']);

_component2['default'].registerComponent('TextTrackSettings', TextTrackSettings);

function getSelectedOptionValue(target) {
  var selectedOption = undefined;
  // not all browsers support selectedOptions, so, fallback to options
  if (target.selectedOptions) {
    selectedOption = target.selectedOptions[0];
  } else if (target.options) {
    selectedOption = target.options[target.options.selectedIndex];
  }

  return selectedOption.value;
}

function setSelectedOption(target, value) {
  if (!value) {
    return;
  }

  var i = undefined;
  for (i = 0; i < target.options.length; i++) {
    var option = target.options[i];
    if (option.value === value) {
      break;
    }
  }

  target.selectedIndex = i;
}

function captionOptionsMenuTemplate() {
  var template = '<div class="vjs-tracksettings">\n      <div class="vjs-tracksettings-colors">\n        <div class="vjs-fg-color vjs-tracksetting">\n            <label class="vjs-label">Foreground</label>\n            <select>\n              <option value="">---</option>\n              <option value="#FFF">White</option>\n              <option value="#000">Black</option>\n              <option value="#F00">Red</option>\n              <option value="#0F0">Green</option>\n              <option value="#00F">Blue</option>\n              <option value="#FF0">Yellow</option>\n              <option value="#F0F">Magenta</option>\n              <option value="#0FF">Cyan</option>\n            </select>\n            <span class="vjs-text-opacity vjs-opacity">\n              <select>\n                <option value="">---</option>\n                <option value="1">Opaque</option>\n                <option value="0.5">Semi-Opaque</option>\n              </select>\n            </span>\n        </div> <!-- vjs-fg-color -->\n        <div class="vjs-bg-color vjs-tracksetting">\n            <label class="vjs-label">Background</label>\n            <select>\n              <option value="">---</option>\n              <option value="#FFF">White</option>\n              <option value="#000">Black</option>\n              <option value="#F00">Red</option>\n              <option value="#0F0">Green</option>\n              <option value="#00F">Blue</option>\n              <option value="#FF0">Yellow</option>\n              <option value="#F0F">Magenta</option>\n              <option value="#0FF">Cyan</option>\n            </select>\n            <span class="vjs-bg-opacity vjs-opacity">\n                <select>\n                  <option value="">---</option>\n                  <option value="1">Opaque</option>\n                  <option value="0.5">Semi-Transparent</option>\n                  <option value="0">Transparent</option>\n                </select>\n            </span>\n        </div> <!-- vjs-bg-color -->\n        <div class="window-color vjs-tracksetting">\n            <label class="vjs-label">Window</label>\n            <select>\n              <option value="">---</option>\n              <option value="#FFF">White</option>\n              <option value="#000">Black</option>\n              <option value="#F00">Red</option>\n              <option value="#0F0">Green</option>\n              <option value="#00F">Blue</option>\n              <option value="#FF0">Yellow</option>\n              <option value="#F0F">Magenta</option>\n              <option value="#0FF">Cyan</option>\n            </select>\n            <span class="vjs-window-opacity vjs-opacity">\n                <select>\n                  <option value="">---</option>\n                  <option value="1">Opaque</option>\n                  <option value="0.5">Semi-Transparent</option>\n                  <option value="0">Transparent</option>\n                </select>\n            </span>\n        </div> <!-- vjs-window-color -->\n      </div> <!-- vjs-tracksettings -->\n      <div class="vjs-tracksettings-font">\n        <div class="vjs-font-percent vjs-tracksetting">\n          <label class="vjs-label">Font Size</label>\n          <select>\n            <option value="0.50">50%</option>\n            <option value="0.75">75%</option>\n            <option value="1.00" selected>100%</option>\n            <option value="1.25">125%</option>\n            <option value="1.50">150%</option>\n            <option value="1.75">175%</option>\n            <option value="2.00">200%</option>\n            <option value="3.00">300%</option>\n            <option value="4.00">400%</option>\n          </select>\n        </div> <!-- vjs-font-percent -->\n        <div class="vjs-edge-style vjs-tracksetting">\n          <label class="vjs-label">Text Edge Style</label>\n          <select>\n            <option value="none">None</option>\n            <option value="raised">Raised</option>\n            <option value="depressed">Depressed</option>\n            <option value="uniform">Uniform</option>\n            <option value="dropshadow">Dropshadow</option>\n          </select>\n        </div> <!-- vjs-edge-style -->\n        <div class="vjs-font-family vjs-tracksetting">\n          <label class="vjs-label">Font Family</label>\n          <select>\n            <option value="">Default</option>\n            <option value="monospaceSerif">Monospace Serif</option>\n            <option value="proportionalSerif">Proportional Serif</option>\n            <option value="monospaceSansSerif">Monospace Sans-Serif</option>\n            <option value="proportionalSansSerif">Proportional Sans-Serif</option>\n            <option value="casual">Casual</option>\n            <option value="script">Script</option>\n            <option value="small-caps">Small Caps</option>\n          </select>\n        </div> <!-- vjs-font-family -->\n      </div>\n    </div>\n    <div class="vjs-tracksettings-controls">\n      <button class="vjs-default-button">Defaults</button>\n      <button class="vjs-done-button">Done</button>\n    </div>';

  return template;
}

exports['default'] = TextTrackSettings;
module.exports = exports['default'];

},{"../component":66,"../utils/events.js":145,"../utils/fn.js":146,"../utils/log.js":149,"global/window":8,"safe-json-parse/tuple":57}],135:[function(_dereq_,module,exports){
/**
 * @file text-track.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _textTrackCueList = _dereq_('./text-track-cue-list');

var _textTrackCueList2 = _interopRequireDefault(_textTrackCueList);

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _trackEnums = _dereq_('./track-enums');

var _utilsLogJs = _dereq_('../utils/log.js');

var _utilsLogJs2 = _interopRequireDefault(_utilsLogJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _trackJs = _dereq_('./track.js');

var _trackJs2 = _interopRequireDefault(_trackJs);

var _utilsUrlJs = _dereq_('../utils/url.js');

var _xhr = _dereq_('xhr');

var _xhr2 = _interopRequireDefault(_xhr);

var _utilsMergeOptions = _dereq_('../utils/merge-options');

var _utilsMergeOptions2 = _interopRequireDefault(_utilsMergeOptions);

var _utilsBrowserJs = _dereq_('../utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

/**
 * takes a webvtt file contents and parses it into cues
 *
 * @param {String} srcContent webVTT file contents
 * @param {Track} track track to addcues to
 */
var parseCues = function parseCues(srcContent, track) {
  var parser = new _globalWindow2['default'].WebVTT.Parser(_globalWindow2['default'], _globalWindow2['default'].vttjs, _globalWindow2['default'].WebVTT.StringDecoder());
  var errors = [];

  parser.oncue = function (cue) {
    track.addCue(cue);
  };

  parser.onparsingerror = function (error) {
    errors.push(error);
  };

  parser.onflush = function () {
    track.trigger({
      type: 'loadeddata',
      target: track
    });
  };

  parser.parse(srcContent);
  if (errors.length > 0) {
    if (console.groupCollapsed) {
      console.groupCollapsed('Text Track parsing errors for ' + track.src);
    }
    errors.forEach(function (error) {
      return _utilsLogJs2['default'].error(error);
    });
    if (console.groupEnd) {
      console.groupEnd();
    }
  }

  parser.flush();
};

/**
 * load a track from a  specifed url
 *
 * @param {String} src url to load track from
 * @param {Track} track track to addcues to
 */
var loadTrack = function loadTrack(src, track) {
  var opts = {
    uri: src
  };
  var crossOrigin = _utilsUrlJs.isCrossOrigin(src);

  if (crossOrigin) {
    opts.cors = crossOrigin;
  }

  _xhr2['default'](opts, Fn.bind(this, function (err, response, responseBody) {
    if (err) {
      return _utilsLogJs2['default'].error(err, response);
    }

    track.loaded_ = true;

    // Make sure that vttjs has loaded, otherwise, wait till it finished loading
    // NOTE: this is only used for the alt/video.novtt.js build
    if (typeof _globalWindow2['default'].WebVTT !== 'function') {
      if (track.tech_) {
        (function () {
          var loadHandler = function loadHandler() {
            return parseCues(responseBody, track);
          };
          track.tech_.on('vttjsloaded', loadHandler);
          track.tech_.on('vttjserror', function () {
            _utilsLogJs2['default'].error('vttjs failed to load, stopping trying to process ' + track.src);
            track.tech_.off('vttjsloaded', loadHandler);
          });
        })();
      }
    } else {
      parseCues(responseBody, track);
    }
  }));
};

/**
 * A single text track as defined in:
 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack
 *
 * interface TextTrack : EventTarget {
 *   readonly attribute TextTrackKind kind;
 *   readonly attribute DOMString label;
 *   readonly attribute DOMString language;
 *
 *   readonly attribute DOMString id;
 *   readonly attribute DOMString inBandMetadataTrackDispatchType;
 *
 *   attribute TextTrackMode mode;
 *
 *   readonly attribute TextTrackCueList? cues;
 *   readonly attribute TextTrackCueList? activeCues;
 *
 *   void addCue(TextTrackCue cue);
 *   void removeCue(TextTrackCue cue);
 *
 *   attribute EventHandler oncuechange;
 * };
 *
 * @param {Object=} options Object of option names and values
 * @extends Track
 * @class TextTrack
 */

var TextTrack = (function (_Track) {
  _inherits(TextTrack, _Track);

  function TextTrack() {
    var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

    _classCallCheck(this, TextTrack);

    if (!options.tech) {
      throw new Error('A tech was not provided.');
    }

    var settings = _utilsMergeOptions2['default'](options, {
      kind: _trackEnums.TextTrackKind[options.kind] || 'subtitles',
      language: options.language || options.srclang || ''
    });
    var mode = _trackEnums.TextTrackMode[settings.mode] || 'disabled';
    var default_ = settings['default'];

    if (settings.kind === 'metadata' || settings.kind === 'chapters') {
      mode = 'hidden';
    }
    // on IE8 this will be a document element
    // for every other browser this will be a normal object
    var tt = _Track.call(this, settings);
    tt.tech_ = settings.tech;

    if (browser.IS_IE8) {
      for (var prop in TextTrack.prototype) {
        if (prop !== 'constructor') {
          tt[prop] = TextTrack.prototype[prop];
        }
      }
    }

    tt.cues_ = [];
    tt.activeCues_ = [];

    var cues = new _textTrackCueList2['default'](tt.cues_);
    var activeCues = new _textTrackCueList2['default'](tt.activeCues_);
    var changed = false;
    var timeupdateHandler = Fn.bind(tt, function () {
      this.activeCues;
      if (changed) {
        this.trigger('cuechange');
        changed = false;
      }
    });

    if (mode !== 'disabled') {
      tt.tech_.on('timeupdate', timeupdateHandler);
    }

    Object.defineProperty(tt, 'default', {
      get: function get() {
        return default_;
      },
      set: function set() {}
    });

    Object.defineProperty(tt, 'mode', {
      get: function get() {
        return mode;
      },
      set: function set(newMode) {
        if (!_trackEnums.TextTrackMode[newMode]) {
          return;
        }
        mode = newMode;
        if (mode === 'showing') {
          this.tech_.on('timeupdate', timeupdateHandler);
        }
        this.trigger('modechange');
      }
    });

    Object.defineProperty(tt, 'cues', {
      get: function get() {
        if (!this.loaded_) {
          return null;
        }

        return cues;
      },
      set: function set() {}
    });

    Object.defineProperty(tt, 'activeCues', {
      get: function get() {
        if (!this.loaded_) {
          return null;
        }

        // nothing to do
        if (this.cues.length === 0) {
          return activeCues;
        }

        var ct = this.tech_.currentTime();
        var active = [];

        for (var i = 0, l = this.cues.length; i < l; i++) {
          var cue = this.cues[i];

          if (cue.startTime <= ct && cue.endTime >= ct) {
            active.push(cue);
          } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) {
            active.push(cue);
          }
        }

        changed = false;

        if (active.length !== this.activeCues_.length) {
          changed = true;
        } else {
          for (var i = 0; i < active.length; i++) {
            if (this.activeCues_.indexOf(active[i]) === -1) {
              changed = true;
            }
          }
        }

        this.activeCues_ = active;
        activeCues.setCues_(this.activeCues_);

        return activeCues;
      },
      set: function set() {}
    });

    if (settings.src) {
      tt.src = settings.src;
      loadTrack(settings.src, tt);
    } else {
      tt.loaded_ = true;
    }

    return tt;
  }

  /**
   * cuechange - One or more cues in the track have become active or stopped being active.
   */

  /**
   * add a cue to the internal list of cues
   *
   * @param {Object} cue the cue to add to our internal list
   * @method addCue
   */

  TextTrack.prototype.addCue = function addCue(cue) {
    var tracks = this.tech_.textTracks();

    if (tracks) {
      for (var i = 0; i < tracks.length; i++) {
        if (tracks[i] !== this) {
          tracks[i].removeCue(cue);
        }
      }
    }

    this.cues_.push(cue);
    this.cues.setCues_(this.cues_);
  };

  /**
   * remvoe a cue from our internal list
   *
   * @param {Object} removeCue the cue to remove from our internal list
   * @method removeCue
   */

  TextTrack.prototype.removeCue = function removeCue(_removeCue) {
    var removed = false;

    for (var i = 0, l = this.cues_.length; i < l; i++) {
      var cue = this.cues_[i];

      if (cue === _removeCue) {
        this.cues_.splice(i, 1);
        removed = true;
      }
    }

    if (removed) {
      this.cues.setCues_(this.cues_);
    }
  };

  return TextTrack;
})(_trackJs2['default']);

TextTrack.prototype.allowedEvents_ = {
  cuechange: 'cuechange'
};

exports['default'] = TextTrack;
module.exports = exports['default'];

},{"../utils/browser.js":141,"../utils/fn.js":146,"../utils/log.js":149,"../utils/merge-options":150,"../utils/url.js":154,"./text-track-cue-list":130,"./track-enums":136,"./track.js":138,"global/document":7,"global/window":8,"xhr":60}],136:[function(_dereq_,module,exports){
/**
 * @file track-kinds.js
 */

/**
 * https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind
 *
 * enum VideoTrackKind {
 *   "alternative",
 *   "captions",
 *   "main",
 *   "sign",
 *   "subtitles",
 *   "commentary",
 *   "",
 * };
 */
'use strict';

exports.__esModule = true;
var VideoTrackKind = {
  alternative: 'alternative',
  captions: 'captions',
  main: 'main',
  sign: 'sign',
  subtitles: 'subtitles',
  commentary: 'commentary'
};

/**
 * https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind
 *
 * enum AudioTrackKind {
 *   "alternative",
 *   "descriptions",
 *   "main",
 *   "main-desc",
 *   "translation",
 *   "commentary",
 *   "",
 * };
 */
var AudioTrackKind = {
  alternative: 'alternative',
  descriptions: 'descriptions',
  main: 'main',
  'main-desc': 'main-desc',
  translation: 'translation',
  commentary: 'commentary'
};

/**
 * https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackkind
 *
 * enum TextTrackKind {
 *   "subtitles",
 *   "captions",
 *   "descriptions",
 *   "chapters",
 *   "metadata"
 * };
 */
var TextTrackKind = {
  subtitles: 'subtitles',
  captions: 'captions',
  descriptions: 'descriptions',
  chapters: 'chapters',
  metadata: 'metadata'
};

/**
 * https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
 *
 * enum TextTrackMode { "disabled",  "hidden",  "showing" };
 */
var TextTrackMode = {
  disabled: 'disabled',
  hidden: 'hidden',
  showing: 'showing'
};

/* jshint ignore:start */
// we ignore jshint here because it does not see
// AudioTrackKind as defined here
exports['default'] = { VideoTrackKind: VideoTrackKind, AudioTrackKind: AudioTrackKind, TextTrackKind: TextTrackKind, TextTrackMode: TextTrackMode };

/* jshint ignore:end */
module.exports = exports['default'];

},{}],137:[function(_dereq_,module,exports){
/**
 * @file track-list.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _eventTarget = _dereq_('../event-target');

var _eventTarget2 = _interopRequireDefault(_eventTarget);

var _utilsFnJs = _dereq_('../utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _utilsBrowserJs = _dereq_('../utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

/**
 * Common functionaliy between Text, Audio, and Video TrackLists
 * Interfaces defined in the following spec:
 * @link https://html.spec.whatwg.org/multipage/embedded-content.html
 *
 * @param {Track[]} tracks A list of tracks to initialize the list with
 * @param {Object} list the child object with inheritance done manually for ie8
 * @extends EventTarget
 * @class TrackList
 */

var TrackList = (function (_EventTarget) {
  _inherits(TrackList, _EventTarget);

  function TrackList() {
    var tracks = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];
    var list = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];

    _classCallCheck(this, TrackList);

    _EventTarget.call(this);
    if (!list) {
      list = this;
      if (browser.IS_IE8) {
        list = _globalDocument2['default'].createElement('custom');
        for (var prop in TrackList.prototype) {
          if (prop !== 'constructor') {
            list[prop] = TrackList.prototype[prop];
          }
        }
      }
    }

    list.tracks_ = [];
    Object.defineProperty(list, 'length', {
      get: function get() {
        return this.tracks_.length;
      }
    });

    for (var i = 0; i < tracks.length; i++) {
      list.addTrack_(tracks[i]);
    }

    return list;
  }

  /**
   * change - One or more tracks in the track list have been enabled or disabled.
   * addtrack - A track has been added to the track list.
   * removetrack - A track has been removed from the track list.
   */

  /**
   * Add a Track from TrackList
   *
   * @param {Mixed} track
   * @method addTrack_
   * @private
   */

  TrackList.prototype.addTrack_ = function addTrack_(track) {
    var index = this.tracks_.length;

    if (!('' + index in this)) {
      Object.defineProperty(this, index, {
        get: function get() {
          return this.tracks_[index];
        }
      });
    }

    // Do not add duplicate tracks
    if (this.tracks_.indexOf(track) === -1) {
      this.tracks_.push(track);
      this.trigger({
        track: track,
        type: 'addtrack'
      });
    }
  };

  /**
   * Remove a Track from TrackList
   *
   * @param {Track} rtrack track to be removed
   * @method removeTrack_
   * @private
   */

  TrackList.prototype.removeTrack_ = function removeTrack_(rtrack) {
    var track = undefined;

    for (var i = 0, l = this.length; i < l; i++) {
      if (this[i] === rtrack) {
        track = this[i];
        if (track.off) {
          track.off();
        }

        this.tracks_.splice(i, 1);

        break;
      }
    }

    if (!track) {
      return;
    }

    this.trigger({
      track: track,
      type: 'removetrack'
    });
  };

  /**
   * Get a Track from the TrackList by a tracks id
   *
   * @param {String} id - the id of the track to get
   * @method getTrackById
   * @return {Track}
   * @private
   */

  TrackList.prototype.getTrackById = function getTrackById(id) {
    var result = null;

    for (var i = 0, l = this.length; i < l; i++) {
      var track = this[i];
      if (track.id === id) {
        result = track;
        break;
      }
    }

    return result;
  };

  return TrackList;
})(_eventTarget2['default']);

TrackList.prototype.allowedEvents_ = {
  change: 'change',
  addtrack: 'addtrack',
  removetrack: 'removetrack'
};

// emulate attribute EventHandler support to allow for feature detection
for (var _event in TrackList.prototype.allowedEvents_) {
  TrackList.prototype['on' + _event] = null;
}

exports['default'] = TrackList;
module.exports = exports['default'];

},{"../event-target":104,"../utils/browser.js":141,"../utils/fn.js":146,"global/document":7}],138:[function(_dereq_,module,exports){
/**
 * @file track.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _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; } }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _utilsBrowserJs = _dereq_('../utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _utilsGuidJs = _dereq_('../utils/guid.js');

var Guid = _interopRequireWildcard(_utilsGuidJs);

var _eventTarget = _dereq_('../event-target');

var _eventTarget2 = _interopRequireDefault(_eventTarget);

/**
 * setup the common parts of an audio, video, or text track
 * @link https://html.spec.whatwg.org/multipage/embedded-content.html
 *
 * @param {String} type The type of track we are dealing with audio|video|text
 * @param {Object=} options Object of option names and values
 * @extends EventTarget
 * @class Track
 */

var Track = (function (_EventTarget) {
  _inherits(Track, _EventTarget);

  function Track() {
    var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

    _classCallCheck(this, Track);

    _EventTarget.call(this);

    var track = this;
    if (browser.IS_IE8) {
      track = _globalDocument2['default'].createElement('custom');
      for (var prop in Track.prototype) {
        if (prop !== 'constructor') {
          track[prop] = Track.prototype[prop];
        }
      }
    }

    var trackProps = {
      id: options.id || 'vjs_track_' + Guid.newGUID(),
      kind: options.kind || '',
      label: options.label || '',
      language: options.language || ''
    };

    var _loop = function (key) {
      Object.defineProperty(track, key, {
        get: function get() {
          return trackProps[key];
        },
        set: function set() {}
      });
    };

    for (var key in trackProps) {
      _loop(key);
    }

    return track;
  }

  return Track;
})(_eventTarget2['default']);

exports['default'] = Track;
module.exports = exports['default'];

},{"../event-target":104,"../utils/browser.js":141,"../utils/guid.js":148,"global/document":7}],139:[function(_dereq_,module,exports){
/**
 * @file video-track-list.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _trackList = _dereq_('./track-list');

var _trackList2 = _interopRequireDefault(_trackList);

var _utilsBrowserJs = _dereq_('../utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

/**
 * disable other video tracks before selecting the new one
 *
 * @param {Array|VideoTrackList} list list to work on
 * @param {VideoTrack} track the track to skip
 */
var disableOthers = function disableOthers(list, track) {
  for (var i = 0; i < list.length; i++) {
    if (track.id === list[i].id) {
      continue;
    }
    // another audio track is enabled, disable it
    list[i].selected = false;
  }
};

/**
* A list of possiblee video tracks. Most functionality is in the
 * base class Tracklist and the spec for VideoTrackList is located at:
 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
 *
 * interface VideoTrackList : EventTarget {
 *   readonly attribute unsigned long length;
 *   getter VideoTrack (unsigned long index);
 *   VideoTrack? getTrackById(DOMString id);
 *   readonly attribute long selectedIndex;
 *
 *   attribute EventHandler onchange;
 *   attribute EventHandler onaddtrack;
 *   attribute EventHandler onremovetrack;
 * };
 *
 * @param {VideoTrack[]} tracks a list of video tracks to instantiate the list with
 # @extends TrackList
 * @class VideoTrackList
 */

var VideoTrackList = (function (_TrackList) {
  _inherits(VideoTrackList, _TrackList);

  function VideoTrackList() {
    var tracks = arguments.length <= 0 || arguments[0] === undefined ? [] : arguments[0];

    _classCallCheck(this, VideoTrackList);

    var list = undefined;

    // make sure only 1 track is enabled
    // sorted from last index to first index
    for (var i = tracks.length - 1; i >= 0; i--) {
      if (tracks[i].selected) {
        disableOthers(tracks, tracks[i]);
        break;
      }
    }

    // IE8 forces us to implement inheritance ourselves
    // as it does not support Object.defineProperty properly
    if (browser.IS_IE8) {
      list = _globalDocument2['default'].createElement('custom');
      for (var prop in _trackList2['default'].prototype) {
        if (prop !== 'constructor') {
          list[prop] = _trackList2['default'].prototype[prop];
        }
      }
      for (var prop in VideoTrackList.prototype) {
        if (prop !== 'constructor') {
          list[prop] = VideoTrackList.prototype[prop];
        }
      }
    }

    list = _TrackList.call(this, tracks, list);
    list.changing_ = false;

    Object.defineProperty(list, 'selectedIndex', {
      get: function get() {
        for (var i = 0; i < this.length; i++) {
          if (this[i].selected) {
            return i;
          }
        }
        return -1;
      },
      set: function set() {}
    });

    return list;
  }

  VideoTrackList.prototype.addTrack_ = function addTrack_(track) {
    var _this = this;

    if (track.selected) {
      disableOthers(this, track);
    }

    _TrackList.prototype.addTrack_.call(this, track);
    // native tracks don't have this
    if (!track.addEventListener) {
      return;
    }
    track.addEventListener('selectedchange', function () {
      if (_this.changing_) {
        return;
      }
      _this.changing_ = true;
      disableOthers(_this, track);
      _this.changing_ = false;
      _this.trigger('change');
    });
  };

  VideoTrackList.prototype.addTrack = function addTrack(track) {
    this.addTrack_(track);
  };

  VideoTrackList.prototype.removeTrack = function removeTrack(track) {
    _TrackList.prototype.removeTrack_.call(this, track);
  };

  return VideoTrackList;
})(_trackList2['default']);

exports['default'] = VideoTrackList;
module.exports = exports['default'];

},{"../utils/browser.js":141,"./track-list":137,"global/document":7}],140:[function(_dereq_,module,exports){
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

function _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; }

var _trackEnums = _dereq_('./track-enums');

var _track = _dereq_('./track');

var _track2 = _interopRequireDefault(_track);

var _utilsMergeOptions = _dereq_('../utils/merge-options');

var _utilsMergeOptions2 = _interopRequireDefault(_utilsMergeOptions);

var _utilsBrowserJs = _dereq_('../utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

/**
 * A single video text track as defined in:
 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack
 *
 * interface VideoTrack {
 *   readonly attribute DOMString id;
 *   readonly attribute DOMString kind;
 *   readonly attribute DOMString label;
 *   readonly attribute DOMString language;
 *   attribute boolean selected;
 * };
 *
 * @param {Object=} options Object of option names and values
 * @class VideoTrack
 */

var VideoTrack = (function (_Track) {
  _inherits(VideoTrack, _Track);

  function VideoTrack() {
    var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

    _classCallCheck(this, VideoTrack);

    var settings = _utilsMergeOptions2['default'](options, {
      kind: _trackEnums.VideoTrackKind[options.kind] || ''
    });

    // on IE8 this will be a document element
    // for every other browser this will be a normal object
    var track = _Track.call(this, settings);
    var selected = false;

    if (browser.IS_IE8) {
      for (var prop in VideoTrack.prototype) {
        if (prop !== 'constructor') {
          track[prop] = VideoTrack.prototype[prop];
        }
      }
    }

    Object.defineProperty(track, 'selected', {
      get: function get() {
        return selected;
      },
      set: function set(newSelected) {
        // an invalid or unchanged value
        if (typeof newSelected !== 'boolean' || newSelected === selected) {
          return;
        }
        selected = newSelected;
        this.trigger('selectedchange');
      }
    });

    // if the user sets this track to selected then
    // set selected to that true value otherwise
    // we keep it false
    if (settings.selected) {
      track.selected = settings.selected;
    }

    return track;
  }

  return VideoTrack;
})(_track2['default']);

exports['default'] = VideoTrack;
module.exports = exports['default'];

},{"../utils/browser.js":141,"../utils/merge-options":150,"./track":138,"./track-enums":136}],141:[function(_dereq_,module,exports){
/**
 * @file browser.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var USER_AGENT = _globalWindow2['default'].navigator.userAgent;
var webkitVersionMap = /AppleWebKit\/([\d.]+)/i.exec(USER_AGENT);
var appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null;

/*
 * Device is an iPhone
 *
 * @type {Boolean}
 * @constant
 * @private
 */
var IS_IPAD = /iPad/i.test(USER_AGENT);

exports.IS_IPAD = IS_IPAD;
// The Facebook app's UIWebView identifies as both an iPhone and iPad, so
// to identify iPhones, we need to exclude iPads.
// http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/
var IS_IPHONE = /iPhone/i.test(USER_AGENT) && !IS_IPAD;
exports.IS_IPHONE = IS_IPHONE;
var IS_IPOD = /iPod/i.test(USER_AGENT);
exports.IS_IPOD = IS_IPOD;
var IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;

exports.IS_IOS = IS_IOS;
var IOS_VERSION = (function () {
  var match = USER_AGENT.match(/OS (\d+)_/i);
  if (match && match[1]) {
    return match[1];
  }
})();

exports.IOS_VERSION = IOS_VERSION;
var IS_ANDROID = /Android/i.test(USER_AGENT);
exports.IS_ANDROID = IS_ANDROID;
var ANDROID_VERSION = (function () {
  // This matches Android Major.Minor.Patch versions
  // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned
  var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i),
      major,
      minor;

  if (!match) {
    return null;
  }

  major = match[1] && parseFloat(match[1]);
  minor = match[2] && parseFloat(match[2]);

  if (major && minor) {
    return parseFloat(match[1] + '.' + match[2]);
  } else if (major) {
    return major;
  } else {
    return null;
  }
})();
exports.ANDROID_VERSION = ANDROID_VERSION;
// Old Android is defined as Version older than 2.3, and requiring a webkit version of the android browser
var IS_OLD_ANDROID = IS_ANDROID && /webkit/i.test(USER_AGENT) && ANDROID_VERSION < 2.3;
exports.IS_OLD_ANDROID = IS_OLD_ANDROID;
var IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;

exports.IS_NATIVE_ANDROID = IS_NATIVE_ANDROID;
var IS_FIREFOX = /Firefox/i.test(USER_AGENT);
exports.IS_FIREFOX = IS_FIREFOX;
var IS_EDGE = /Edge/i.test(USER_AGENT);
exports.IS_EDGE = IS_EDGE;
var IS_CHROME = !IS_EDGE && /Chrome/i.test(USER_AGENT);
exports.IS_CHROME = IS_CHROME;
var IS_IE8 = /MSIE\s8\.0/.test(USER_AGENT);

exports.IS_IE8 = IS_IE8;
var IE_VERSION = (function () {
  var result = /MSIE\s(\d+)\.\d/.exec(USER_AGENT);
  var version = result && parseFloat(result[1]);

  if (!version && /Trident\/7.0/i.test(USER_AGENT) && /rv:11.0/.test(USER_AGENT)) {
    // IE 11 has a different user agent string than other IE versions
    version = 11.0;
  }

  return version;
})();

exports.IE_VERSION = IE_VERSION;
var IS_SAFARI = /Safari/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE;
exports.IS_SAFARI = IS_SAFARI;
var IS_ANY_SAFARI = IS_SAFARI || IS_IOS;

exports.IS_ANY_SAFARI = IS_ANY_SAFARI;
var TOUCH_ENABLED = !!('ontouchstart' in _globalWindow2['default'] || _globalWindow2['default'].DocumentTouch && _globalDocument2['default'] instanceof _globalWindow2['default'].DocumentTouch);
exports.TOUCH_ENABLED = TOUCH_ENABLED;
var BACKGROUND_SIZE_SUPPORTED = ('backgroundSize' in _globalDocument2['default'].createElement('video').style);
exports.BACKGROUND_SIZE_SUPPORTED = BACKGROUND_SIZE_SUPPORTED;

},{"global/document":7,"global/window":8}],142:[function(_dereq_,module,exports){
/**
 * @file buffer.js
 */
'use strict';

exports.__esModule = true;
exports.bufferedPercent = bufferedPercent;

var _timeRangesJs = _dereq_('./time-ranges.js');

/**
 * Compute how much your video has been buffered
 *
 * @param  {Object} Buffered object
 * @param  {Number} Total duration
 * @return {Number} Percent buffered of the total duration
 * @private
 * @function bufferedPercent
 */

function bufferedPercent(buffered, duration) {
  var bufferedDuration = 0,
      start,
      end;

  if (!duration) {
    return 0;
  }

  if (!buffered || !buffered.length) {
    buffered = _timeRangesJs.createTimeRange(0, 0);
  }

  for (var i = 0; i < buffered.length; i++) {
    start = buffered.start(i);
    end = buffered.end(i);

    // buffered end can be bigger than duration by a very small fraction
    if (end > duration) {
      end = duration;
    }

    bufferedDuration += end - start;
  }

  return bufferedDuration / duration;
}

},{"./time-ranges.js":152}],143:[function(_dereq_,module,exports){
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _logJs = _dereq_('./log.js');

var _logJs2 = _interopRequireDefault(_logJs);

/**
 * Object containing the default behaviors for available handler methods.
 *
 * @private
 * @type {Object}
 */
var defaultBehaviors = {
  get: function get(obj, key) {
    return obj[key];
  },
  set: function set(obj, key, value) {
    obj[key] = value;
    return true;
  }
};

/**
 * Expose private objects publicly using a Proxy to log deprecation warnings.
 *
 * Browsers that do not support Proxy objects will simply return the `target`
 * object, so it can be directly exposed.
 *
 * @param {Object} target The target object.
 * @param {Object} messages Messages to display from a Proxy. Only operations
 *                          with an associated message will be proxied.
 * @param {String} [messages.get]
 * @param {String} [messages.set]
 * @return {Object} A Proxy if supported or the `target` argument.
 */

exports['default'] = function (target) {
  var messages = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];

  if (typeof Proxy === 'function') {
    var _ret = (function () {
      var handler = {};

      // Build a handler object based on those keys that have both messages
      // and default behaviors.
      Object.keys(messages).forEach(function (key) {
        if (defaultBehaviors.hasOwnProperty(key)) {
          handler[key] = function () {
            _logJs2['default'].warn(messages[key]);
            return defaultBehaviors[key].apply(this, arguments);
          };
        }
      });

      return {
        v: new Proxy(target, handler)
      };
    })();

    if (typeof _ret === 'object') return _ret.v;
  }
  return target;
};

module.exports = exports['default'];

},{"./log.js":149}],144:[function(_dereq_,module,exports){
/**
 * @file dom.js
 */
'use strict';

exports.__esModule = true;
exports.getEl = getEl;
exports.createEl = createEl;
exports.textContent = textContent;
exports.insertElFirst = insertElFirst;
exports.getElData = getElData;
exports.hasElData = hasElData;
exports.removeElData = removeElData;
exports.hasElClass = hasElClass;
exports.addElClass = addElClass;
exports.removeElClass = removeElClass;
exports.toggleElClass = toggleElClass;
exports.setElAttributes = setElAttributes;
exports.getElAttributes = getElAttributes;
exports.blockTextSelection = blockTextSelection;
exports.unblockTextSelection = unblockTextSelection;
exports.findElPosition = findElPosition;
exports.getPointerPosition = getPointerPosition;
exports.isEl = isEl;
exports.isTextNode = isTextNode;
exports.emptyEl = emptyEl;
exports.normalizeContent = normalizeContent;
exports.appendContent = appendContent;
exports.insertContent = insertContent;

var _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 ', '.']);

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _taggedTemplateLiteralLoose(strings, raw) { strings.raw = raw; return strings; }

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _guidJs = _dereq_('./guid.js');

var Guid = _interopRequireWildcard(_guidJs);

var _logJs = _dereq_('./log.js');

var _logJs2 = _interopRequireDefault(_logJs);

var _tsml = _dereq_('tsml');

var _tsml2 = _interopRequireDefault(_tsml);

/**
 * Detect if a value is a string with any non-whitespace characters.
 *
 * @param  {String} str
 * @return {Boolean}
 */
function isNonBlankString(str) {
  return typeof str === 'string' && /\S/.test(str);
}

/**
 * Throws an error if the passed string has whitespace. This is used by
 * class methods to be relatively consistent with the classList API.
 *
 * @param  {String} str
 * @return {Boolean}
 */
function throwIfWhitespace(str) {
  if (/\s/.test(str)) {
    throw new Error('class has illegal whitespace characters');
  }
}

/**
 * Produce a regular expression for matching a class name.
 *
 * @param  {String} className
 * @return {RegExp}
 */
function classRegExp(className) {
  return new RegExp('(^|\\s)' + className + '($|\\s)');
}

/**
 * Creates functions to query the DOM using a given method.
 *
 * @function createQuerier
 * @private
 * @param  {String} method
 * @return {Function}
 */
function createQuerier(method) {
  return function (selector, context) {
    if (!isNonBlankString(selector)) {
      return _globalDocument2['default'][method](null);
    }
    if (isNonBlankString(context)) {
      context = _globalDocument2['default'].querySelector(context);
    }
    return (isEl(context) ? context : _globalDocument2['default'])[method](selector);
  };
}

/**
 * Shorthand for document.getElementById()
 * Also allows for CSS (jQuery) ID syntax. But nothing other than IDs.
 *
 * @param  {String} id  Element ID
 * @return {Element}    Element with supplied ID
 * @function getEl
 */

function getEl(id) {
  if (id.indexOf('#') === 0) {
    id = id.slice(1);
  }

  return _globalDocument2['default'].getElementById(id);
}

/**
 * Creates an element and applies properties.
 *
 * @param  {String} [tagName='div'] Name of tag to be created.
 * @param  {Object} [properties={}] Element properties to be applied.
 * @param  {Object} [attributes={}] Element attributes to be applied.
 * @return {Element}
 * @function createEl
 */

function createEl() {
  var tagName = arguments.length <= 0 || arguments[0] === undefined ? 'div' : arguments[0];
  var properties = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
  var attributes = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2];

  var el = _globalDocument2['default'].createElement(tagName);

  Object.getOwnPropertyNames(properties).forEach(function (propName) {
    var val = properties[propName];

    // See #2176
    // We originally were accepting both properties and attributes in the
    // same object, but that doesn't work so well.
    if (propName.indexOf('aria-') !== -1 || propName === 'role' || propName === 'type') {
      _logJs2['default'].warn(_tsml2['default'](_templateObject, propName, val));
      el.setAttribute(propName, val);
    } else {
      el[propName] = val;
    }
  });

  Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
    var val = attributes[attrName];
    el.setAttribute(attrName, attributes[attrName]);
  });

  return el;
}

/**
 * Injects text into an element, replacing any existing contents entirely.
 *
 * @param  {Element} el
 * @param  {String} text
 * @return {Element}
 * @function textContent
 */

function textContent(el, text) {
  if (typeof el.textContent === 'undefined') {
    el.innerText = text;
  } else {
    el.textContent = text;
  }
}

/**
 * Insert an element as the first child node of another
 *
 * @param  {Element} child   Element to insert
 * @param  {Element} parent Element to insert child into
 * @private
 * @function insertElFirst
 */

function insertElFirst(child, parent) {
  if (parent.firstChild) {
    parent.insertBefore(child, parent.firstChild);
  } else {
    parent.appendChild(child);
  }
}

/**
 * Element Data Store. Allows for binding data to an element without putting it directly on the element.
 * Ex. Event listeners are stored here.
 * (also from jsninja.com, slightly modified and updated for closure compiler)
 *
 * @type {Object}
 * @private
 */
var elData = {};

/*
 * Unique attribute name to store an element's guid in
 *
 * @type {String}
 * @constant
 * @private
 */
var elIdAttr = 'vdata' + new Date().getTime();

/**
 * Returns the cache object where data for an element is stored
 *
 * @param  {Element} el Element to store data for.
 * @return {Object}
 * @function getElData
 */

function getElData(el) {
  var id = el[elIdAttr];

  if (!id) {
    id = el[elIdAttr] = Guid.newGUID();
  }

  if (!elData[id]) {
    elData[id] = {};
  }

  return elData[id];
}

/**
 * Returns whether or not an element has cached data
 *
 * @param  {Element} el A dom element
 * @return {Boolean}
 * @private
 * @function hasElData
 */

function hasElData(el) {
  var id = el[elIdAttr];

  if (!id) {
    return false;
  }

  return !!Object.getOwnPropertyNames(elData[id]).length;
}

/**
 * Delete data for the element from the cache and the guid attr from getElementById
 *
 * @param  {Element} el Remove data for an element
 * @private
 * @function removeElData
 */

function removeElData(el) {
  var id = el[elIdAttr];

  if (!id) {
    return;
  }

  // Remove all stored data
  delete elData[id];

  // Remove the elIdAttr property from the DOM node
  try {
    delete el[elIdAttr];
  } catch (e) {
    if (el.removeAttribute) {
      el.removeAttribute(elIdAttr);
    } else {
      // IE doesn't appear to support removeAttribute on the document element
      el[elIdAttr] = null;
    }
  }
}

/**
 * Check if an element has a CSS class
 *
 * @function hasElClass
 * @param {Element} element Element to check
 * @param {String} classToCheck Classname to check
 */

function hasElClass(element, classToCheck) {
  throwIfWhitespace(classToCheck);
  if (element.classList) {
    return element.classList.contains(classToCheck);
  } else {
    return classRegExp(classToCheck).test(element.className);
  }
}

/**
 * Add a CSS class name to an element
 *
 * @function addElClass
 * @param {Element} element    Element to add class name to
 * @param {String} classToAdd Classname to add
 */

function addElClass(element, classToAdd) {
  if (element.classList) {
    element.classList.add(classToAdd);

    // Don't need to `throwIfWhitespace` here because `hasElClass` will do it
    // in the case of classList not being supported.
  } else if (!hasElClass(element, classToAdd)) {
      element.className = (element.className + ' ' + classToAdd).trim();
    }

  return element;
}

/**
 * Remove a CSS class name from an element
 *
 * @function removeElClass
 * @param {Element} element    Element to remove from class name
 * @param {String} classToRemove Classname to remove
 */

function removeElClass(element, classToRemove) {
  if (element.classList) {
    element.classList.remove(classToRemove);
  } else {
    throwIfWhitespace(classToRemove);
    element.className = element.className.split(/\s+/).filter(function (c) {
      return c !== classToRemove;
    }).join(' ');
  }

  return element;
}

/**
 * Adds or removes a CSS class name on an element depending on an optional
 * condition or the presence/absence of the class name.
 *
 * @function toggleElClass
 * @param    {Element} element
 * @param    {String} classToToggle
 * @param    {Boolean|Function} [predicate]
 *           Can be a function that returns a Boolean. If `true`, the class
 *           will be added; if `false`, the class will be removed. If not
 *           given, the class will be added if not present and vice versa.
 */

function toggleElClass(element, classToToggle, predicate) {

  // This CANNOT use `classList` internally because IE does not support the
  // second parameter to the `classList.toggle()` method! Which is fine because
  // `classList` will be used by the add/remove functions.
  var has = hasElClass(element, classToToggle);

  if (typeof predicate === 'function') {
    predicate = predicate(element, classToToggle);
  }

  if (typeof predicate !== 'boolean') {
    predicate = !has;
  }

  // If the necessary class operation matches the current state of the
  // element, no action is required.
  if (predicate === has) {
    return;
  }

  if (predicate) {
    addElClass(element, classToToggle);
  } else {
    removeElClass(element, classToToggle);
  }

  return element;
}

/**
 * Apply attributes to an HTML element.
 *
 * @param  {Element} el         Target element.
 * @param  {Object=} attributes Element attributes to be applied.
 * @private
 * @function setElAttributes
 */

function setElAttributes(el, attributes) {
  Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
    var attrValue = attributes[attrName];

    if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {
      el.removeAttribute(attrName);
    } else {
      el.setAttribute(attrName, attrValue === true ? '' : attrValue);
    }
  });
}

/**
 * Get an element's attribute values, as defined on the HTML tag
 * Attributes are not the same as properties. They're defined on the tag
 * or with setAttribute (which shouldn't be used with HTML)
 * This will return true or false for boolean attributes.
 *
 * @param  {Element} tag Element from which to get tag attributes
 * @return {Object}
 * @private
 * @function getElAttributes
 */

function getElAttributes(tag) {
  var obj, knownBooleans, attrs, attrName, attrVal;

  obj = {};

  // known boolean attributes
  // we can check for matching boolean properties, but older browsers
  // won't know about HTML5 boolean attributes that we still read from
  knownBooleans = ',' + 'autoplay,controls,loop,muted,default' + ',';

  if (tag && tag.attributes && tag.attributes.length > 0) {
    attrs = tag.attributes;

    for (var i = attrs.length - 1; i >= 0; i--) {
      attrName = attrs[i].name;
      attrVal = attrs[i].value;

      // check for known booleans
      // the matching element property will return a value for typeof
      if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) {
        // the value of an included boolean attribute is typically an empty
        // string ('') which would equal false if we just check for a false value.
        // we also don't want support bad code like autoplay='false'
        attrVal = attrVal !== null ? true : false;
      }

      obj[attrName] = attrVal;
    }
  }

  return obj;
}

/**
 * Attempt to block the ability to select text while dragging controls
 *
 * @return {Boolean}
 * @function blockTextSelection
 */

function blockTextSelection() {
  _globalDocument2['default'].body.focus();
  _globalDocument2['default'].onselectstart = function () {
    return false;
  };
}

/**
 * Turn off text selection blocking
 *
 * @return {Boolean}
 * @function unblockTextSelection
 */

function unblockTextSelection() {
  _globalDocument2['default'].onselectstart = function () {
    return true;
  };
}

/**
 * Offset Left
 * getBoundingClientRect technique from
 * John Resig http://ejohn.org/blog/getboundingclientrect-is-awesome/
 *
 * @function findElPosition
 * @param {Element} el Element from which to get offset
 * @return {Object}
 */

function findElPosition(el) {
  var box = undefined;

  if (el.getBoundingClientRect && el.parentNode) {
    box = el.getBoundingClientRect();
  }

  if (!box) {
    return {
      left: 0,
      top: 0
    };
  }

  var docEl = _globalDocument2['default'].documentElement;
  var body = _globalDocument2['default'].body;

  var clientLeft = docEl.clientLeft || body.clientLeft || 0;
  var scrollLeft = _globalWindow2['default'].pageXOffset || body.scrollLeft;
  var left = box.left + scrollLeft - clientLeft;

  var clientTop = docEl.clientTop || body.clientTop || 0;
  var scrollTop = _globalWindow2['default'].pageYOffset || body.scrollTop;
  var top = box.top + scrollTop - clientTop;

  // Android sometimes returns slightly off decimal values, so need to round
  return {
    left: Math.round(left),
    top: Math.round(top)
  };
}

/**
 * Get pointer position in element
 * Returns an object with x and y coordinates.
 * The base on the coordinates are the bottom left of the element.
 *
 * @function getPointerPosition
 * @param {Element} el Element on which to get the pointer position on
 * @param {Event} event Event object
 * @return {Object} This object will have x and y coordinates corresponding to the mouse position
 */

function getPointerPosition(el, event) {
  var position = {};
  var box = findElPosition(el);
  var boxW = el.offsetWidth;
  var boxH = el.offsetHeight;

  var boxY = box.top;
  var boxX = box.left;
  var pageY = event.pageY;
  var pageX = event.pageX;

  if (event.changedTouches) {
    pageX = event.changedTouches[0].pageX;
    pageY = event.changedTouches[0].pageY;
  }

  position.y = Math.max(0, Math.min(1, (boxY - pageY + boxH) / boxH));
  position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));

  return position;
}

/**
 * Determines, via duck typing, whether or not a value is a DOM element.
 *
 * @function isEl
 * @param    {Mixed} value
 * @return   {Boolean}
 */

function isEl(value) {
  return !!value && typeof value === 'object' && value.nodeType === 1;
}

/**
 * Determines, via duck typing, whether or not a value is a text node.
 *
 * @param  {Mixed} value
 * @return {Boolean}
 */

function isTextNode(value) {
  return !!value && typeof value === 'object' && value.nodeType === 3;
}

/**
 * Empties the contents of an element.
 *
 * @function emptyEl
 * @param    {Element} el
 * @return   {Element}
 */

function emptyEl(el) {
  while (el.firstChild) {
    el.removeChild(el.firstChild);
  }
  return el;
}

/**
 * Normalizes content for eventual insertion into the DOM.
 *
 * This allows a wide range of content definition methods, but protects
 * from falling into the trap of simply writing to `innerHTML`, which is
 * an XSS concern.
 *
 * The content for an element can be passed in multiple types and
 * combinations, whose behavior is as follows:
 *
 * - String
 *   Normalized into a text node.
 *
 * - Element, TextNode
 *   Passed through.
 *
 * - Array
 *   A one-dimensional array of strings, elements, nodes, or functions (which
 *   return single strings, elements, or nodes).
 *
 * - Function
 *   If the sole argument, is expected to produce a string, element,
 *   node, or array.
 *
 * @function normalizeContent
 * @param    {String|Element|TextNode|Array|Function} content
 * @return   {Array}
 */

function normalizeContent(content) {

  // First, invoke content if it is a function. If it produces an array,
  // that needs to happen before normalization.
  if (typeof content === 'function') {
    content = content();
  }

  // Next up, normalize to an array, so one or many items can be normalized,
  // filtered, and returned.
  return (Array.isArray(content) ? content : [content]).map(function (value) {

    // First, invoke value if it is a function to produce a new value,
    // which will be subsequently normalized to a Node of some kind.
    if (typeof value === 'function') {
      value = value();
    }

    if (isEl(value) || isTextNode(value)) {
      return value;
    }

    if (typeof value === 'string' && /\S/.test(value)) {
      return _globalDocument2['default'].createTextNode(value);
    }
  }).filter(function (value) {
    return value;
  });
}

/**
 * Normalizes and appends content to an element.
 *
 * @function appendContent
 * @param    {Element} el
 * @param    {String|Element|TextNode|Array|Function} content
 *           See: `normalizeContent`
 * @return   {Element}
 */

function appendContent(el, content) {
  normalizeContent(content).forEach(function (node) {
    return el.appendChild(node);
  });
  return el;
}

/**
 * Normalizes and inserts content into an element; this is identical to
 * `appendContent()`, except it empties the element first.
 *
 * @function insertContent
 * @param    {Element} el
 * @param    {String|Element|TextNode|Array|Function} content
 *           See: `normalizeContent`
 * @return   {Element}
 */

function insertContent(el, content) {
  return appendContent(emptyEl(el), content);
}

/**
 * Finds a single DOM element matching `selector` within the optional
 * `context` of another DOM element (defaulting to `document`).
 *
 * @function $
 * @param    {String} selector
 *           A valid CSS selector, which will be passed to `querySelector`.
 *
 * @param    {Element|String} [context=document]
 *           A DOM element within which to query. Can also be a selector
 *           string in which case the first matching element will be used
 *           as context. If missing (or no element matches selector), falls
 *           back to `document`.
 *
 * @return   {Element|null}
 */
var $ = createQuerier('querySelector');

exports.$ = $;
/**
 * Finds a all DOM elements matching `selector` within the optional
 * `context` of another DOM element (defaulting to `document`).
 *
 * @function $$
 * @param    {String} selector
 *           A valid CSS selector, which will be passed to `querySelectorAll`.
 *
 * @param    {Element|String} [context=document]
 *           A DOM element within which to query. Can also be a selector
 *           string in which case the first matching element will be used
 *           as context. If missing (or no element matches selector), falls
 *           back to `document`.
 *
 * @return   {NodeList}
 */
var $$ = createQuerier('querySelectorAll');
exports.$$ = $$;

},{"./guid.js":148,"./log.js":149,"global/document":7,"global/window":8,"tsml":59}],145:[function(_dereq_,module,exports){
/**
 * @file events.js
 *
 * Event System (John Resig - Secrets of a JS Ninja http://jsninja.com/)
 * (Original book version wasn't completely usable, so fixed some things and made Closure Compiler compatible)
 * This should work very similarly to jQuery's events, however it's based off the book version which isn't as
 * robust as jquery's, so there's probably some differences.
 */

'use strict';

exports.__esModule = true;
exports.on = on;
exports.off = off;
exports.trigger = trigger;
exports.one = one;
exports.fixEvent = fixEvent;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _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; } }

var _domJs = _dereq_('./dom.js');

var Dom = _interopRequireWildcard(_domJs);

var _guidJs = _dereq_('./guid.js');

var Guid = _interopRequireWildcard(_guidJs);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

/**
 * Add an event listener to element
 * It stores the handler function in a separate cache object
 * and adds a generic handler to the element's event,
 * along with a unique id (guid) to the element.
 *
 * @param  {Element|Object}   elem Element or object to bind listeners to
 * @param  {String|Array}   type Type of event to bind to.
 * @param  {Function} fn   Event listener.
 * @method on
 */

function on(elem, type, fn) {
  if (Array.isArray(type)) {
    return _handleMultipleEvents(on, elem, type, fn);
  }

  var data = Dom.getElData(elem);

  // We need a place to store all our handler data
  if (!data.handlers) data.handlers = {};

  if (!data.handlers[type]) data.handlers[type] = [];

  if (!fn.guid) fn.guid = Guid.newGUID();

  data.handlers[type].push(fn);

  if (!data.dispatcher) {
    data.disabled = false;

    data.dispatcher = function (event, hash) {

      if (data.disabled) return;
      event = fixEvent(event);

      var handlers = data.handlers[event.type];

      if (handlers) {
        // Copy handlers so if handlers are added/removed during the process it doesn't throw everything off.
        var handlersCopy = handlers.slice(0);

        for (var m = 0, n = handlersCopy.length; m < n; m++) {
          if (event.isImmediatePropagationStopped()) {
            break;
          } else {
            handlersCopy[m].call(elem, event, hash);
          }
        }
      }
    };
  }

  if (data.handlers[type].length === 1) {
    if (elem.addEventListener) {
      elem.addEventListener(type, data.dispatcher, false);
    } else if (elem.attachEvent) {
      elem.attachEvent('on' + type, data.dispatcher);
    }
  }
}

/**
 * Removes event listeners from an element
 *
 * @param  {Element|Object}   elem Object to remove listeners from
 * @param  {String|Array=}   type Type of listener to remove. Don't include to remove all events from element.
 * @param  {Function} fn   Specific listener to remove. Don't include to remove listeners for an event type.
 * @method off
 */

function off(elem, type, fn) {
  // Don't want to add a cache object through getElData if not needed
  if (!Dom.hasElData(elem)) return;

  var data = Dom.getElData(elem);

  // If no events exist, nothing to unbind
  if (!data.handlers) {
    return;
  }

  if (Array.isArray(type)) {
    return _handleMultipleEvents(off, elem, type, fn);
  }

  // Utility function
  var removeType = function removeType(t) {
    data.handlers[t] = [];
    _cleanUpEvents(elem, t);
  };

  // Are we removing all bound events?
  if (!type) {
    for (var t in data.handlers) {
      removeType(t);
    }return;
  }

  var handlers = data.handlers[type];

  // If no handlers exist, nothing to unbind
  if (!handlers) return;

  // If no listener was provided, remove all listeners for type
  if (!fn) {
    removeType(type);
    return;
  }

  // We're only removing a single handler
  if (fn.guid) {
    for (var n = 0; n < handlers.length; n++) {
      if (handlers[n].guid === fn.guid) {
        handlers.splice(n--, 1);
      }
    }
  }

  _cleanUpEvents(elem, type);
}

/**
 * Trigger an event for an element
 *
 * @param  {Element|Object}      elem  Element to trigger an event on
 * @param  {Event|Object|String} event A string (the type) or an event object with a type attribute
 * @param  {Object} [hash] data hash to pass along with the event
 * @return {Boolean=} Returned only if default was prevented
 * @method trigger
 */

function trigger(elem, event, hash) {
  // Fetches element data and a reference to the parent (for bubbling).
  // Don't want to add a data object to cache for every parent,
  // so checking hasElData first.
  var elemData = Dom.hasElData(elem) ? Dom.getElData(elem) : {};
  var parent = elem.parentNode || elem.ownerDocument;
  // type = event.type || event,
  // handler;

  // If an event name was passed as a string, creates an event out of it
  if (typeof event === 'string') {
    event = { type: event, target: elem };
  }
  // Normalizes the event properties.
  event = fixEvent(event);

  // If the passed element has a dispatcher, executes the established handlers.
  if (elemData.dispatcher) {
    elemData.dispatcher.call(elem, event, hash);
  }

  // Unless explicitly stopped or the event does not bubble (e.g. media events)
  // recursively calls this function to bubble the event up the DOM.
  if (parent && !event.isPropagationStopped() && event.bubbles === true) {
    trigger.call(null, parent, event, hash);

    // If at the top of the DOM, triggers the default action unless disabled.
  } else if (!parent && !event.defaultPrevented) {
      var targetData = Dom.getElData(event.target);

      // Checks if the target has a default action for this event.
      if (event.target[event.type]) {
        // Temporarily disables event dispatching on the target as we have already executed the handler.
        targetData.disabled = true;
        // Executes the default action.
        if (typeof event.target[event.type] === 'function') {
          event.target[event.type]();
        }
        // Re-enables event dispatching.
        targetData.disabled = false;
      }
    }

  // Inform the triggerer if the default was prevented by returning false
  return !event.defaultPrevented;
}

/**
 * Trigger a listener only once for an event
 *
 * @param  {Element|Object}   elem Element or object to
 * @param  {String|Array}   type Name/type of event
 * @param  {Function} fn Event handler function
 * @method one
 */

function one(elem, type, fn) {
  if (Array.isArray(type)) {
    return _handleMultipleEvents(one, elem, type, fn);
  }
  var func = function func() {
    off(elem, type, func);
    fn.apply(this, arguments);
  };
  // copy the guid to the new function so it can removed using the original function's ID
  func.guid = fn.guid = fn.guid || Guid.newGUID();
  on(elem, type, func);
}

/**
 * Fix a native event to have standard property values
 *
 * @param  {Object} event Event object to fix
 * @return {Object}
 * @private
 * @method fixEvent
 */

function fixEvent(event) {

  function returnTrue() {
    return true;
  }
  function returnFalse() {
    return false;
  }

  // Test if fixing up is needed
  // Used to check if !event.stopPropagation instead of isPropagationStopped
  // But native events return true for stopPropagation, but don't have
  // other expected methods like isPropagationStopped. Seems to be a problem
  // with the Javascript Ninja code. So we're just overriding all events now.
  if (!event || !event.isPropagationStopped) {
    var old = event || _globalWindow2['default'].event;

    event = {};
    // Clone the old object so that we can modify the values event = {};
    // IE8 Doesn't like when you mess with native event properties
    // Firefox returns false for event.hasOwnProperty('type') and other props
    //  which makes copying more difficult.
    // TODO: Probably best to create a whitelist of event props
    for (var key in old) {
      // Safari 6.0.3 warns you if you try to copy deprecated layerX/Y
      // Chrome warns you if you try to copy deprecated keyboardEvent.keyLocation
      // and webkitMovementX/Y
      if (key !== 'layerX' && key !== 'layerY' && key !== 'keyLocation' && key !== 'webkitMovementX' && key !== 'webkitMovementY') {
        // Chrome 32+ warns if you try to copy deprecated returnValue, but
        // we still want to if preventDefault isn't supported (IE8).
        if (!(key === 'returnValue' && old.preventDefault)) {
          event[key] = old[key];
        }
      }
    }

    // The event occurred on this element
    if (!event.target) {
      event.target = event.srcElement || _globalDocument2['default'];
    }

    // Handle which other element the event is related to
    if (!event.relatedTarget) {
      event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
    }

    // Stop the default browser action
    event.preventDefault = function () {
      if (old.preventDefault) {
        old.preventDefault();
      }
      event.returnValue = false;
      old.returnValue = false;
      event.defaultPrevented = true;
    };

    event.defaultPrevented = false;

    // Stop the event from bubbling
    event.stopPropagation = function () {
      if (old.stopPropagation) {
        old.stopPropagation();
      }
      event.cancelBubble = true;
      old.cancelBubble = true;
      event.isPropagationStopped = returnTrue;
    };

    event.isPropagationStopped = returnFalse;

    // Stop the event from bubbling and executing other handlers
    event.stopImmediatePropagation = function () {
      if (old.stopImmediatePropagation) {
        old.stopImmediatePropagation();
      }
      event.isImmediatePropagationStopped = returnTrue;
      event.stopPropagation();
    };

    event.isImmediatePropagationStopped = returnFalse;

    // Handle mouse position
    if (event.clientX != null) {
      var doc = _globalDocument2['default'].documentElement,
          body = _globalDocument2['default'].body;

      event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
      event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
    }

    // Handle key presses
    event.which = event.charCode || event.keyCode;

    // Fix button for mouse clicks:
    // 0 == left; 1 == middle; 2 == right
    if (event.button != null) {
      event.button = event.button & 1 ? 0 : event.button & 4 ? 1 : event.button & 2 ? 2 : 0;
    }
  }

  // Returns fixed-up instance
  return event;
}

/**
 * Clean up the listener cache and dispatchers
*
 * @param  {Element|Object} elem Element to clean up
 * @param  {String} type Type of event to clean up
 * @private
 * @method _cleanUpEvents
 */
function _cleanUpEvents(elem, type) {
  var data = Dom.getElData(elem);

  // Remove the events of a particular type if there are none left
  if (data.handlers[type].length === 0) {
    delete data.handlers[type];
    // data.handlers[type] = null;
    // Setting to null was causing an error with data.handlers

    // Remove the meta-handler from the element
    if (elem.removeEventListener) {
      elem.removeEventListener(type, data.dispatcher, false);
    } else if (elem.detachEvent) {
      elem.detachEvent('on' + type, data.dispatcher);
    }
  }

  // Remove the events object if there are no types left
  if (Object.getOwnPropertyNames(data.handlers).length <= 0) {
    delete data.handlers;
    delete data.dispatcher;
    delete data.disabled;
  }

  // Finally remove the element data if there is no data left
  if (Object.getOwnPropertyNames(data).length === 0) {
    Dom.removeElData(elem);
  }
}

/**
 * Loops through an array of event types and calls the requested method for each type.
 *
 * @param  {Function} fn   The event method we want to use.
 * @param  {Element|Object} elem Element or object to bind listeners to
 * @param  {String}   type Type of event to bind to.
 * @param  {Function} callback   Event listener.
 * @private
 * @function _handleMultipleEvents
 */
function _handleMultipleEvents(fn, elem, types, callback) {
  types.forEach(function (type) {
    //Call the event method for each one of the types
    fn(elem, type, callback);
  });
}

},{"./dom.js":144,"./guid.js":148,"global/document":7,"global/window":8}],146:[function(_dereq_,module,exports){
/**
 * @file fn.js
 */
'use strict';

exports.__esModule = true;

var _guidJs = _dereq_('./guid.js');

/**
 * Bind (a.k.a proxy or Context). A simple method for changing the context of a function
 * It also stores a unique id on the function so it can be easily removed from events
 *
 * @param  {*}   context The object to bind as scope
 * @param  {Function} fn      The function to be bound to a scope
 * @param  {Number=}   uid     An optional unique ID for the function to be set
 * @return {Function}
 * @private
 * @method bind
 */
var bind = function bind(context, fn, uid) {
  // Make sure the function has a unique ID
  if (!fn.guid) {
    fn.guid = _guidJs.newGUID();
  }

  // Create the new function that changes the context
  var ret = function ret() {
    return fn.apply(context, arguments);
  };

  // Allow for the ability to individualize this function
  // Needed in the case where multiple objects might share the same prototype
  // IF both items add an event listener with the same function, then you try to remove just one
  // it will remove both because they both have the same guid.
  // when using this, you need to use the bind method when you remove the listener as well.
  // currently used in text tracks
  ret.guid = uid ? uid + '_' + fn.guid : fn.guid;

  return ret;
};
exports.bind = bind;

},{"./guid.js":148}],147:[function(_dereq_,module,exports){
/**
 * @file format-time.js
 *
 * Format seconds as a time string, H:MM:SS or M:SS
 * Supplying a guide (in seconds) will force a number of leading zeros
 * to cover the length of the guide
 *
 * @param  {Number} seconds Number of seconds to be turned into a string
 * @param  {Number} guide   Number (in seconds) to model the string after
 * @return {String}         Time formatted as H:MM:SS or M:SS
 * @private
 * @function formatTime
 */
'use strict';

exports.__esModule = true;
function formatTime(seconds) {
  var guide = arguments.length <= 1 || arguments[1] === undefined ? seconds : arguments[1];
  return (function () {
    seconds = seconds < 0 ? 0 : seconds;
    var s = Math.floor(seconds % 60);
    var m = Math.floor(seconds / 60 % 60);
    var h = Math.floor(seconds / 3600);
    var gm = Math.floor(guide / 60 % 60);
    var gh = Math.floor(guide / 3600);

    // handle invalid times
    if (isNaN(seconds) || seconds === Infinity) {
      // '-' is false for all relational operators (e.g. <, >=) so this setting
      // will add the minimum number of fields specified by the guide
      h = m = s = '-';
    }

    // Check if we need to show hours
    h = h > 0 || gh > 0 ? h + ':' : '';

    // If hours are showing, we may need to add a leading zero.
    // Always show at least one digit of minutes.
    m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':';

    // Check if leading zero is need for seconds
    s = s < 10 ? '0' + s : s;

    return h + m + s;
  })();
}

exports['default'] = formatTime;
module.exports = exports['default'];

},{}],148:[function(_dereq_,module,exports){
/**
 * @file guid.js
 *
 * Unique ID for an element or function
 * @type {Number}
 * @private
 */
"use strict";

exports.__esModule = true;
exports.newGUID = newGUID;
var _guid = 1;

/**
 * Get the next unique ID
 *
 * @return {String} 
 * @function newGUID
 */

function newGUID() {
  return _guid++;
}

},{}],149:[function(_dereq_,module,exports){
/**
 * @file log.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

/**
 * Log plain debug messages
 */
var log = function log() {
  _logType(null, arguments);
};

/**
 * Keep a history of log messages
 * @type {Array}
 */
log.history = [];

/**
 * Log error messages
 */
log.error = function () {
  _logType('error', arguments);
};

/**
 * Log warning messages
 */
log.warn = function () {
  _logType('warn', arguments);
};

/**
 * Log messages to the console and history based on the type of message
 *
 * @param  {String} type The type of message, or `null` for `log`
 * @param  {Object} args The args to be passed to the log
 * @private
 * @method _logType
 */
function _logType(type, args) {
  // convert args to an array to get array functions
  var argsArray = Array.prototype.slice.call(args);
  // if there's no console then don't try to output messages
  // they will still be stored in log.history
  // Was setting these once outside of this function, but containing them
  // in the function makes it easier to test cases where console doesn't exist
  var noop = function noop() {};

  var console = _globalWindow2['default']['console'] || {
    'log': noop,
    'warn': noop,
    'error': noop
  };

  if (type) {
    // add the type to the front of the message
    argsArray.unshift(type.toUpperCase() + ':');
  } else {
    // default to log with no prefix
    type = 'log';
  }

  // add to history
  log.history.push(argsArray);

  // add console prefix after adding to history
  argsArray.unshift('VIDEOJS:');

  // call appropriate log function
  if (console[type].apply) {
    console[type].apply(console, argsArray);
  } else {
    // ie8 doesn't allow error.apply, but it will just join() the array anyway
    console[type](argsArray.join(' '));
  }
}

exports['default'] = log;
module.exports = exports['default'];

},{"global/window":8}],150:[function(_dereq_,module,exports){
/**
 * @file merge-options.js
 */
'use strict';

exports.__esModule = true;
exports['default'] = mergeOptions;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _lodashCompatObjectMerge = _dereq_('lodash-compat/object/merge');

var _lodashCompatObjectMerge2 = _interopRequireDefault(_lodashCompatObjectMerge);

function isPlain(obj) {
  return !!obj && typeof obj === 'object' && obj.toString() === '[object Object]' && obj.constructor === Object;
}

/**
 * Merge customizer. video.js simply overwrites non-simple objects
 * (like arrays) instead of attempting to overlay them.
 * @see https://lodash.com/docs#merge
 */
var customizer = function customizer(destination, source) {
  // If we're not working with a plain object, copy the value as is
  // If source is an array, for instance, it will replace destination
  if (!isPlain(source)) {
    return source;
  }

  // If the new value is a plain object but the first object value is not
  // we need to create a new object for the first object to merge with.
  // This makes it consistent with how merge() works by default
  // and also protects from later changes the to first object affecting
  // the second object's values.
  if (!isPlain(destination)) {
    return mergeOptions(source);
  }
};

/**
 * Merge one or more options objects, recursively merging **only**
 * plain object properties.  Previously `deepMerge`.
 *
 * @param  {...Object} source One or more objects to merge
 * @returns {Object}          a new object that is the union of all
 * provided objects
 * @function mergeOptions
 */

function mergeOptions() {
  // contruct the call dynamically to handle the variable number of
  // objects to merge
  var args = Array.prototype.slice.call(arguments);

  // unshift an empty object into the front of the call as the target
  // of the merge
  args.unshift({});

  // customize conflict resolution to match our historical merge behavior
  args.push(customizer);

  _lodashCompatObjectMerge2['default'].apply(null, args);

  // return the mutated result object
  return args[0];
}

module.exports = exports['default'];

},{"lodash-compat/object/merge":46}],151:[function(_dereq_,module,exports){
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var createStyleElement = function createStyleElement(className) {
  var style = _globalDocument2['default'].createElement('style');
  style.className = className;

  return style;
};

exports.createStyleElement = createStyleElement;
var setTextContent = function setTextContent(el, content) {
  if (el.styleSheet) {
    el.styleSheet.cssText = content;
  } else {
    el.textContent = content;
  }
};
exports.setTextContent = setTextContent;

},{"global/document":7}],152:[function(_dereq_,module,exports){
'use strict';

exports.__esModule = true;
exports.createTimeRanges = createTimeRanges;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _logJs = _dereq_('./log.js');

var _logJs2 = _interopRequireDefault(_logJs);

/**
 * @file time-ranges.js
 *
 * Should create a fake TimeRange object
 * Mimics an HTML5 time range instance, which has functions that
 * return the start and end times for a range
 * TimeRanges are returned by the buffered() method
 *
 * @param  {(Number|Array)} Start of a single range or an array of ranges
 * @param  {Number} End of a single range
 * @private
 * @method createTimeRanges
 */

function createTimeRanges(start, end) {
  if (Array.isArray(start)) {
    return createTimeRangesObj(start);
  } else if (start === undefined || end === undefined) {
    return createTimeRangesObj();
  }
  return createTimeRangesObj([[start, end]]);
}

exports.createTimeRange = createTimeRanges;

function createTimeRangesObj(ranges) {
  if (ranges === undefined || ranges.length === 0) {
    return {
      length: 0,
      start: function start() {
        throw new Error('This TimeRanges object is empty');
      },
      end: function end() {
        throw new Error('This TimeRanges object is empty');
      }
    };
  }
  return {
    length: ranges.length,
    start: getRange.bind(null, 'start', 0, ranges),
    end: getRange.bind(null, 'end', 1, ranges)
  };
}

function getRange(fnName, valueIndex, ranges, rangeIndex) {
  if (rangeIndex === undefined) {
    _logJs2['default'].warn('DEPRECATED: Function \'' + fnName + '\' on \'TimeRanges\' called without an index argument.');
    rangeIndex = 0;
  }
  rangeCheck(fnName, rangeIndex, ranges.length - 1);
  return ranges[rangeIndex][valueIndex];
}

function rangeCheck(fnName, index, maxIndex) {
  if (index < 0 || index > maxIndex) {
    throw new Error('Failed to execute \'' + fnName + '\' on \'TimeRanges\': The index provided (' + index + ') is greater than or equal to the maximum bound (' + maxIndex + ').');
  }
}

},{"./log.js":149}],153:[function(_dereq_,module,exports){
/**
 * @file to-title-case.js
 *
 * Uppercase the first letter of a string
 *
 * @param  {String} string String to be uppercased
 * @return {String}
 * @private
 * @method toTitleCase
 */
"use strict";

exports.__esModule = true;
function toTitleCase(string) {
  return string.charAt(0).toUpperCase() + string.slice(1);
}

exports["default"] = toTitleCase;
module.exports = exports["default"];

},{}],154:[function(_dereq_,module,exports){
/**
 * @file url.js
 */
'use strict';

exports.__esModule = true;

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

/**
 * Resolve and parse the elements of a URL
 *
 * @param  {String} url The url to parse
 * @return {Object}     An object of url details
 * @method parseUrl
 */
var parseUrl = function parseUrl(url) {
  var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host'];

  // add the url to an anchor and let the browser parse the URL
  var a = _globalDocument2['default'].createElement('a');
  a.href = url;

  // IE8 (and 9?) Fix
  // ie8 doesn't parse the URL correctly until the anchor is actually
  // added to the body, and an innerHTML is needed to trigger the parsing
  var addToBody = a.host === '' && a.protocol !== 'file:';
  var div = undefined;
  if (addToBody) {
    div = _globalDocument2['default'].createElement('div');
    div.innerHTML = '<a href="' + url + '"></a>';
    a = div.firstChild;
    // prevent the div from affecting layout
    div.setAttribute('style', 'display:none; position:absolute;');
    _globalDocument2['default'].body.appendChild(div);
  }

  // Copy the specific URL properties to a new object
  // This is also needed for IE8 because the anchor loses its
  // properties when it's removed from the dom
  var details = {};
  for (var i = 0; i < props.length; i++) {
    details[props[i]] = a[props[i]];
  }

  // IE9 adds the port to the host property unlike everyone else. If
  // a port identifier is added for standard ports, strip it.
  if (details.protocol === 'http:') {
    details.host = details.host.replace(/:80$/, '');
  }
  if (details.protocol === 'https:') {
    details.host = details.host.replace(/:443$/, '');
  }

  if (addToBody) {
    _globalDocument2['default'].body.removeChild(div);
  }

  return details;
};

exports.parseUrl = parseUrl;
/**
 * Get absolute version of relative URL. Used to tell flash correct URL.
 * http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
 *
 * @param  {String} url URL to make absolute
 * @return {String}     Absolute URL
 * @private
 * @method getAbsoluteURL
 */
var getAbsoluteURL = function getAbsoluteURL(url) {
  // Check if absolute URL
  if (!url.match(/^https?:\/\//)) {
    // Convert to absolute URL. Flash hosted off-site needs an absolute URL.
    var div = _globalDocument2['default'].createElement('div');
    div.innerHTML = '<a href="' + url + '">x</a>';
    url = div.firstChild.href;
  }

  return url;
};

exports.getAbsoluteURL = getAbsoluteURL;
/**
 * Returns the extension of the passed file name. It will return an empty string if you pass an invalid path
 *
 * @param {String}    path    The fileName path like '/path/to/file.mp4'
 * @returns {String}          The extension in lower case or an empty string if no extension could be found.
 * @method getFileExtension
 */
var getFileExtension = function getFileExtension(path) {
  if (typeof path === 'string') {
    var splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i;
    var pathParts = splitPathRe.exec(path);

    if (pathParts) {
      return pathParts.pop().toLowerCase();
    }
  }

  return '';
};

exports.getFileExtension = getFileExtension;
/**
 * Returns whether the url passed is a cross domain request or not.
 *
 * @param {String} url The url to check
 * @return {Boolean}   Whether it is a cross domain request or not
 * @method isCrossOrigin
 */
var isCrossOrigin = function isCrossOrigin(url) {
  var winLoc = _globalWindow2['default'].location;
  var urlInfo = parseUrl(url);

  // IE8 protocol relative urls will return ':' for protocol
  var srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol;

  // Check if url is for another domain/origin
  // IE8 doesn't know location.origin, so we won't rely on it here
  var crossOrigin = srcProtocol + urlInfo.host !== winLoc.protocol + winLoc.host;

  return crossOrigin;
};
exports.isCrossOrigin = isCrossOrigin;

},{"global/document":7,"global/window":8}],155:[function(_dereq_,module,exports){
/**
 * @file video.js
 */
'use strict';

exports.__esModule = true;

function _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; } }

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _globalWindow = _dereq_('global/window');

var _globalWindow2 = _interopRequireDefault(_globalWindow);

var _globalDocument = _dereq_('global/document');

var _globalDocument2 = _interopRequireDefault(_globalDocument);

var _setup = _dereq_('./setup');

var setup = _interopRequireWildcard(_setup);

var _utilsStylesheetJs = _dereq_('./utils/stylesheet.js');

var stylesheet = _interopRequireWildcard(_utilsStylesheetJs);

var _component = _dereq_('./component');

var _component2 = _interopRequireDefault(_component);

var _eventTarget = _dereq_('./event-target');

var _eventTarget2 = _interopRequireDefault(_eventTarget);

var _utilsEventsJs = _dereq_('./utils/events.js');

var Events = _interopRequireWildcard(_utilsEventsJs);

var _player = _dereq_('./player');

var _player2 = _interopRequireDefault(_player);

var _pluginsJs = _dereq_('./plugins.js');

var _pluginsJs2 = _interopRequireDefault(_pluginsJs);

var _srcJsUtilsMergeOptionsJs = _dereq_('../../src/js/utils/merge-options.js');

var _srcJsUtilsMergeOptionsJs2 = _interopRequireDefault(_srcJsUtilsMergeOptionsJs);

var _utilsFnJs = _dereq_('./utils/fn.js');

var Fn = _interopRequireWildcard(_utilsFnJs);

var _tracksTextTrackJs = _dereq_('./tracks/text-track.js');

var _tracksTextTrackJs2 = _interopRequireDefault(_tracksTextTrackJs);

var _tracksAudioTrackJs = _dereq_('./tracks/audio-track.js');

var _tracksAudioTrackJs2 = _interopRequireDefault(_tracksAudioTrackJs);

var _tracksVideoTrackJs = _dereq_('./tracks/video-track.js');

var _tracksVideoTrackJs2 = _interopRequireDefault(_tracksVideoTrackJs);

var _objectAssign = _dereq_('object.assign');

var _objectAssign2 = _interopRequireDefault(_objectAssign);

var _utilsTimeRangesJs = _dereq_('./utils/time-ranges.js');

var _utilsFormatTimeJs = _dereq_('./utils/format-time.js');

var _utilsFormatTimeJs2 = _interopRequireDefault(_utilsFormatTimeJs);

var _utilsLogJs = _dereq_('./utils/log.js');

var _utilsLogJs2 = _interopRequireDefault(_utilsLogJs);

var _utilsDomJs = _dereq_('./utils/dom.js');

var Dom = _interopRequireWildcard(_utilsDomJs);

var _utilsBrowserJs = _dereq_('./utils/browser.js');

var browser = _interopRequireWildcard(_utilsBrowserJs);

var _utilsUrlJs = _dereq_('./utils/url.js');

var Url = _interopRequireWildcard(_utilsUrlJs);

var _extendJs = _dereq_('./extend.js');

var _extendJs2 = _interopRequireDefault(_extendJs);

var _lodashCompatObjectMerge = _dereq_('lodash-compat/object/merge');

var _lodashCompatObjectMerge2 = _interopRequireDefault(_lodashCompatObjectMerge);

var _utilsCreateDeprecationProxyJs = _dereq_('./utils/create-deprecation-proxy.js');

var _utilsCreateDeprecationProxyJs2 = _interopRequireDefault(_utilsCreateDeprecationProxyJs);

var _xhr = _dereq_('xhr');

var _xhr2 = _interopRequireDefault(_xhr);

// Include the built-in techs

var _techTechJs = _dereq_('./tech/tech.js');

var _techTechJs2 = _interopRequireDefault(_techTechJs);

var _techHtml5Js = _dereq_('./tech/html5.js');

var _techHtml5Js2 = _interopRequireDefault(_techHtml5Js);

var _techFlashJs = _dereq_('./tech/flash.js');

var _techFlashJs2 = _interopRequireDefault(_techFlashJs);

// HTML5 Element Shim for IE8
if (typeof HTMLVideoElement === 'undefined') {
  _globalDocument2['default'].createElement('video');
  _globalDocument2['default'].createElement('audio');
  _globalDocument2['default'].createElement('track');
}

/**
 * Doubles as the main function for users to create a player instance and also
 * the main library object.
 * The `videojs` function can be used to initialize or retrieve a player.
 * ```js
 *     var myPlayer = videojs('my_video_id');
 * ```
 *
 * @param  {String|Element} id      Video element or video element ID
 * @param  {Object=} options        Optional options object for config/settings
 * @param  {Function=} ready        Optional ready callback
 * @return {Player}                 A player instance
 * @mixes videojs
 * @method videojs
 */
var videojs = function videojs(id, options, ready) {
  var tag = undefined; // Element of ID

  // Allow for element or ID to be passed in
  // String ID
  if (typeof id === 'string') {

    // Adjust for jQuery ID syntax
    if (id.indexOf('#') === 0) {
      id = id.slice(1);
    }

    // If a player instance has already been created for this ID return it.
    if (videojs.getPlayers()[id]) {

      // If options or ready funtion are passed, warn
      if (options) {
        _utilsLogJs2['default'].warn('Player "' + id + '" is already initialised. Options will not be applied.');
      }

      if (ready) {
        videojs.getPlayers()[id].ready(ready);
      }

      return videojs.getPlayers()[id];

      // Otherwise get element for ID
    } else {
        tag = Dom.getEl(id);
      }

    // ID is a media element
  } else {
      tag = id;
    }

  // Check for a useable element
  if (!tag || !tag.nodeName) {
    // re: nodeName, could be a box div also
    throw new TypeError('The element or ID supplied is not valid. (videojs)'); // Returns
  }

  // Element may have a player attr referring to an already created player instance.
  // If not, set up a new player and return the instance.
  return tag['player'] || _player2['default'].players[tag.playerId] || new _player2['default'](tag, options, ready);
};

// Add default styles
if (_globalWindow2['default'].VIDEOJS_NO_DYNAMIC_STYLE !== true) {
  var style = Dom.$('.vjs-styles-defaults');

  if (!style) {
    style = stylesheet.createStyleElement('vjs-styles-defaults');
    var head = Dom.$('head');
    head.insertBefore(style, head.firstChild);
    stylesheet.setTextContent(style, '\n      .video-js {\n        width: 300px;\n        height: 150px;\n      }\n\n      .vjs-fluid {\n        padding-top: 56.25%\n      }\n    ');
  }
}

// Run Auto-load players
// You have to wait at least once in case this script is loaded after your video in the DOM (weird behavior only with minified version)
setup.autoSetupTimeout(1, videojs);

/*
 * Current software version (semver)
 *
 * @type {String}
 */
videojs.VERSION = '5.10.2';

/**
 * Indicates it's the Hola version of video.js
 *
 * @type {boolean}
 */
videojs.HOLA_VERSION = true;

/**
 * The global options object. These are the settings that take effect
 * if no overrides are specified when the player is created.
 *
 * ```js
 *     videojs.options.autoplay = true
 *     // -> all players will autoplay by default
 * ```
 *
 * @type {Object}
 */
videojs.options = _player2['default'].prototype.options_;

/**
 * Get an object with the currently created players, keyed by player ID
 *
 * @return {Object} The created players
 * @mixes videojs
 * @method getPlayers
 */
videojs.getPlayers = function () {
  return _player2['default'].players;
};

/**
 * For backward compatibility, expose players object.
 *
 * @deprecated
 * @memberOf videojs
 * @property {Object|Proxy} players
 */
videojs.players = _utilsCreateDeprecationProxyJs2['default'](_player2['default'].players, {
  get: 'Access to videojs.players is deprecated; use videojs.getPlayers instead',
  set: 'Modification of videojs.players is deprecated'
});

/**
 * Get a component class object by name
 * ```js
 *     var VjsButton = videojs.getComponent('Button');
 *     // Create a new instance of the component
 *     var myButton = new VjsButton(myPlayer);
 * ```
 *
 * @return {Component} Component identified by name
 * @mixes videojs
 * @method getComponent
 */
videojs.getComponent = _component2['default'].getComponent;

/**
 * Register a component so it can referred to by name
 * Used when adding to other
 * components, either through addChild
 * `component.addChild('myComponent')`
 * or through default children options
 * `{ children: ['myComponent'] }`.
 * ```js
 *     // Get a component to subclass
 *     var VjsButton = videojs.getComponent('Button');
 *     // Subclass the component (see 'extend' doc for more info)
 *     var MySpecialButton = videojs.extend(VjsButton, {});
 *     // Register the new component
 *     VjsButton.registerComponent('MySepcialButton', MySepcialButton);
 *     // (optionally) add the new component as a default player child
 *     myPlayer.addChild('MySepcialButton');
 * ```
 * NOTE: You could also just initialize the component before adding.
 * `component.addChild(new MyComponent());`
 *
 * @param {String} The class name of the component
 * @param {Component} The component class
 * @return {Component} The newly registered component
 * @mixes videojs
 * @method registerComponent
 */
videojs.registerComponent = function (name, comp) {
  if (_techTechJs2['default'].isTech(comp)) {
    _utilsLogJs2['default'].warn('The ' + name + ' tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)');
  }

  _component2['default'].registerComponent.call(_component2['default'], name, comp);
};

/**
 * Get a Tech class object by name
 * ```js
 *     var Html5 = videojs.getTech('Html5');
 *     // Create a new instance of the component
 *     var html5 = new Html5(options);
 * ```
 *
 * @return {Tech} Tech identified by name
 * @mixes videojs
 * @method getComponent
 */
videojs.getTech = _techTechJs2['default'].getTech;

/**
 * Register a Tech so it can referred to by name.
 * This is used in the tech order for the player.
 *
 * ```js
 *     // get the Html5 Tech
 *     var Html5 = videojs.getTech('Html5');
 *     var MyTech = videojs.extend(Html5, {});
 *     // Register the new Tech
 *     VjsButton.registerTech('Tech', MyTech);
 *     var player = videojs('myplayer', {
 *       techOrder: ['myTech', 'html5']
 *     });
 * ```
 *
 * @param {String} The class name of the tech
 * @param {Tech} The tech class
 * @return {Tech} The newly registered Tech
 * @mixes videojs
 * @method registerTech
 */
videojs.registerTech = _techTechJs2['default'].registerTech;

/**
 * A suite of browser and device tests
 *
 * @type {Object}
 * @private
 */
videojs.browser = browser;

/**
 * Whether or not the browser supports touch events. Included for backward
 * compatibility with 4.x, but deprecated. Use `videojs.browser.TOUCH_ENABLED`
 * instead going forward.
 *
 * @deprecated
 * @type {Boolean}
 */
videojs.TOUCH_ENABLED = browser.TOUCH_ENABLED;

/**
 * Subclass an existing class
 * Mimics ES6 subclassing with the `extend` keyword
 * ```js
 *     // Create a basic javascript 'class'
 *     function MyClass(name){
 *       // Set a property at initialization
 *       this.myName = name;
 *     }
 *     // Create an instance method
 *     MyClass.prototype.sayMyName = function(){
 *       alert(this.myName);
 *     };
 *     // Subclass the exisitng class and change the name
 *     // when initializing
 *     var MySubClass = videojs.extend(MyClass, {
 *       constructor: function(name) {
 *         // Call the super class constructor for the subclass
 *         MyClass.call(this, name)
 *       }
 *     });
 *     // Create an instance of the new sub class
 *     var myInstance = new MySubClass('John');
 *     myInstance.sayMyName(); // -> should alert "John"
 * ```
 *
 * @param {Function} The Class to subclass
 * @param {Object} An object including instace methods for the new class
 *                   Optionally including a `constructor` function
 * @return {Function} The newly created subclass
 * @mixes videojs
 * @method extend
 */
videojs.extend = _extendJs2['default'];

/**
 * Merge two options objects recursively
 * Performs a deep merge like lodash.merge but **only merges plain objects**
 * (not arrays, elements, anything else)
 * Other values will be copied directly from the second object.
 * ```js
 *     var defaultOptions = {
 *       foo: true,
 *       bar: {
 *         a: true,
 *         b: [1,2,3]
 *       }
 *     };
 *     var newOptions = {
 *       foo: false,
 *       bar: {
 *         b: [4,5,6]
 *       }
 *     };
 *     var result = videojs.mergeOptions(defaultOptions, newOptions);
 *     // result.foo = false;
 *     // result.bar.a = true;
 *     // result.bar.b = [4,5,6];
 * ```
 *
 * @param {Object} defaults  The options object whose values will be overriden
 * @param {Object} overrides The options object with values to override the first
 * @param {Object} etc       Any number of additional options objects
 *
 * @return {Object} a new object with the merged values
 * @mixes videojs
 * @method mergeOptions
 */
videojs.mergeOptions = _srcJsUtilsMergeOptionsJs2['default'];

/**
 * Change the context (this) of a function
 *
 *     videojs.bind(newContext, function(){
 *       this === newContext
 *     });
 *
 * NOTE: as of v5.0 we require an ES5 shim, so you should use the native
 * `function(){}.bind(newContext);` instead of this.
 *
 * @param  {*}        context The object to bind as scope
 * @param  {Function} fn      The function to be bound to a scope
 * @param  {Number=}  uid     An optional unique ID for the function to be set
 * @return {Function}
 */
videojs.bind = Fn.bind;

/**
 * Create a Video.js player plugin
 * Plugins are only initialized when options for the plugin are included
 * in the player options, or the plugin function on the player instance is
 * called.
 * **See the plugin guide in the docs for a more detailed example**
 * ```js
 *     // Make a plugin that alerts when the player plays
 *     videojs.plugin('myPlugin', function(myPluginOptions) {
 *       myPluginOptions = myPluginOptions || {};
 *
 *       var player = this;
 *       var alertText = myPluginOptions.text || 'Player is playing!'
 *
 *       player.on('play', function(){
 *         alert(alertText);
 *       });
 *     });
 *     // USAGE EXAMPLES
 *     // EXAMPLE 1: New player with plugin options, call plugin immediately
 *     var player1 = videojs('idOne', {
 *       myPlugin: {
 *         text: 'Custom text!'
 *       }
 *     });
 *     // Click play
 *     // --> Should alert 'Custom text!'
 *     // EXAMPLE 3: New player, initialize plugin later
 *     var player3 = videojs('idThree');
 *     // Click play
 *     // --> NO ALERT
 *     // Click pause
 *     // Initialize plugin using the plugin function on the player instance
 *     player3.myPlugin({
 *       text: 'Plugin added later!'
 *     });
 *     // Click play
 *     // --> Should alert 'Plugin added later!'
 * ```
 *
 * @param {String} name The plugin name
 * @param {Function} fn The plugin function that will be called with options
 * @mixes videojs
 * @method plugin
 */
videojs.plugin = _pluginsJs2['default'];

/**
 * Adding languages so that they're available to all players.
 * ```js
 *     videojs.addLanguage('es', { 'Hello': 'Hola' });
 * ```
 *
 * @param  {String} code The language code or dictionary property
 * @param  {Object} data The data values to be translated
 * @return {Object} The resulting language dictionary object
 * @mixes videojs
 * @method addLanguage
 */
videojs.addLanguage = function (code, data) {
  var _merge;

  code = ('' + code).toLowerCase();
  return _lodashCompatObjectMerge2['default'](videojs.options.languages, (_merge = {}, _merge[code] = data, _merge))[code];
};

/**
 * Log debug messages.
 *
 * @param {...Object} messages One or more messages to log
 */
videojs.log = _utilsLogJs2['default'];

/**
 * Creates an emulated TimeRange object.
 *
 * @param  {Number|Array} start Start time in seconds or an array of ranges
 * @param  {Number} end   End time in seconds
 * @return {Object}       Fake TimeRange object
 * @method createTimeRange
 */
videojs.createTimeRange = videojs.createTimeRanges = _utilsTimeRangesJs.createTimeRanges;

/**
 * Format seconds as a time string, H:MM:SS or M:SS
 * Supplying a guide (in seconds) will force a number of leading zeros
 * to cover the length of the guide
 *
 * @param  {Number} seconds Number of seconds to be turned into a string
 * @param  {Number} guide   Number (in seconds) to model the string after
 * @return {String}         Time formatted as H:MM:SS or M:SS
 * @method formatTime
 */
videojs.formatTime = _utilsFormatTimeJs2['default'];

/**
 * Resolve and parse the elements of a URL
 *
 * @param  {String} url The url to parse
 * @return {Object}     An object of url details
 * @method parseUrl
 */
videojs.parseUrl = Url.parseUrl;

/**
 * Returns whether the url passed is a cross domain request or not.
 *
 * @param {String} url The url to check
 * @return {Boolean}   Whether it is a cross domain request or not
 * @method isCrossOrigin
 */
videojs.isCrossOrigin = Url.isCrossOrigin;

/**
 * Event target class.
 *
 * @type {Function}
 */
videojs.EventTarget = _eventTarget2['default'];

/**
 * Add an event listener to element
 * It stores the handler function in a separate cache object
 * and adds a generic handler to the element's event,
 * along with a unique id (guid) to the element.
 *
 * @param  {Element|Object}   elem Element or object to bind listeners to
 * @param  {String|Array}   type Type of event to bind to.
 * @param  {Function} fn   Event listener.
 * @method on
 */
videojs.on = Events.on;

/**
 * Trigger a listener only once for an event
 *
 * @param  {Element|Object}   elem Element or object to
 * @param  {String|Array}   type Name/type of event
 * @param  {Function} fn Event handler function
 * @method one
 */
videojs.one = Events.one;

/**
 * Removes event listeners from an element
 *
 * @param  {Element|Object}   elem Object to remove listeners from
 * @param  {String|Array=}   type Type of listener to remove. Don't include to remove all events from element.
 * @param  {Function} fn   Specific listener to remove. Don't include to remove listeners for an event type.
 * @method off
 */
videojs.off = Events.off;

/**
 * Trigger an event for an element
 *
 * @param  {Element|Object}      elem  Element to trigger an event on
 * @param  {Event|Object|String} event A string (the type) or an event object with a type attribute
 * @param  {Object} [hash] data hash to pass along with the event
 * @return {Boolean=} Returned only if default was prevented
 * @method trigger
 */
videojs.trigger = Events.trigger;

/**
 * A cross-browser XMLHttpRequest wrapper. Here's a simple example:
 *
 *     videojs.xhr({
 *       body: someJSONString,
 *       uri: "/foo",
 *       headers: {
 *         "Content-Type": "application/json"
 *       }
 *     }, function (err, resp, body) {
 *       // check resp.statusCode
 *     });
 *
 * Check out the [full
 * documentation](https://github.com/Raynos/xhr/blob/v2.1.0/README.md)
 * for more options.
 *
 * @param {Object} options settings for the request.
 * @return {XMLHttpRequest|XDomainRequest} the request object.
 * @see https://github.com/Raynos/xhr
 */
videojs.xhr = _xhr2['default'];

/**
 * TextTrack class
 *
 * @type {Function}
 */
videojs.TextTrack = _tracksTextTrackJs2['default'];

/**
 * export the AudioTrack class so that source handlers can create
 * AudioTracks and then add them to the players AudioTrackList
 *
 * @type {Function}
 */
videojs.AudioTrack = _tracksAudioTrackJs2['default'];

/**
 * export the VideoTrack class so that source handlers can create
 * VideoTracks and then add them to the players VideoTrackList
 *
 * @type {Function}
 */
videojs.VideoTrack = _tracksVideoTrackJs2['default'];

/**
 * Determines, via duck typing, whether or not a value is a DOM element.
 *
 * @method isEl
 * @param  {Mixed} value
 * @return {Boolean}
 */
videojs.isEl = Dom.isEl;

/**
 * Determines, via duck typing, whether or not a value is a text node.
 *
 * @method isTextNode
 * @param  {Mixed} value
 * @return {Boolean}
 */
videojs.isTextNode = Dom.isTextNode;

/**
 * Creates an element and applies properties.
 *
 * @method createEl
 * @param  {String} [tagName='div'] Name of tag to be created.
 * @param  {Object} [properties={}] Element properties to be applied.
 * @param  {Object} [attributes={}] Element attributes to be applied.
 * @return {Element}
 */
videojs.createEl = Dom.createEl;

/**
 * Check if an element has a CSS class
 *
 * @method hasClass
 * @param {Element} element Element to check
 * @param {String} classToCheck Classname to check
 */
videojs.hasClass = Dom.hasElClass;

/**
 * Add a CSS class name to an element
 *
 * @method addClass
 * @param {Element} element    Element to add class name to
 * @param {String} classToAdd Classname to add
 */
videojs.addClass = Dom.addElClass;

/**
 * Remove a CSS class name from an element
 *
 * @method removeClass
 * @param {Element} element    Element to remove from class name
 * @param {String} classToRemove Classname to remove
 */
videojs.removeClass = Dom.removeElClass;

/**
 * Adds or removes a CSS class name on an element depending on an optional
 * condition or the presence/absence of the class name.
 *
 * @method toggleElClass
 * @param  {Element} element
 * @param  {String} classToToggle
 * @param  {Boolean|Function} [predicate]
 *         Can be a function that returns a Boolean. If `true`, the class
 *         will be added; if `false`, the class will be removed. If not
 *         given, the class will be added if not present and vice versa.
 */
videojs.toggleClass = Dom.toggleElClass;

/**
 * Apply attributes to an HTML element.
 *
 * @method setAttributes
 * @param  {Element} el         Target element.
 * @param  {Object=} attributes Element attributes to be applied.
 */
videojs.setAttributes = Dom.setElAttributes;

/**
 * Get an element's attribute values, as defined on the HTML tag
 * Attributes are not the same as properties. They're defined on the tag
 * or with setAttribute (which shouldn't be used with HTML)
 * This will return true or false for boolean attributes.
 *
 * @method getAttributes
 * @param  {Element} tag Element from which to get tag attributes
 * @return {Object}
 */
videojs.getAttributes = Dom.getElAttributes;

/**
 * Empties the contents of an element.
 *
 * @method emptyEl
 * @param  {Element} el
 * @return {Element}
 */
videojs.emptyEl = Dom.emptyEl;

/**
 * Normalizes and appends content to an element.
 *
 * The content for an element can be passed in multiple types and
 * combinations, whose behavior is as follows:
 *
 * - String
 *   Normalized into a text node.
 *
 * - Element, TextNode
 *   Passed through.
 *
 * - Array
 *   A one-dimensional array of strings, elements, nodes, or functions (which
 *   return single strings, elements, or nodes).
 *
 * - Function
 *   If the sole argument, is expected to produce a string, element,
 *   node, or array.
 *
 * @method appendContent
 * @param  {Element} el
 * @param  {String|Element|TextNode|Array|Function} content
 * @return {Element}
 */
videojs.appendContent = Dom.appendContent;

/**
 * Normalizes and inserts content into an element; this is identical to
 * `appendContent()`, except it empties the element first.
 *
 * The content for an element can be passed in multiple types and
 * combinations, whose behavior is as follows:
 *
 * - String
 *   Normalized into a text node.
 *
 * - Element, TextNode
 *   Passed through.
 *
 * - Array
 *   A one-dimensional array of strings, elements, nodes, or functions (which
 *   return single strings, elements, or nodes).
 *
 * - Function
 *   If the sole argument, is expected to produce a string, element,
 *   node, or array.
 *
 * @method insertContent
 * @param  {Element} el
 * @param  {String|Element|TextNode|Array|Function} content
 * @return {Element}
 */
videojs.insertContent = Dom.insertContent;

/**
 * Export the Dom utilities for use in external plugins
 * and Tech's
 */
videojs.dom = Dom;

/*
 * Custom Universal Module Definition (UMD)
 *
 * Video.js will never be a non-browser lib so we can simplify UMD a bunch and
 * still support requirejs and browserify. This also needs to be closure
 * compiler compatible, so string keys are used.
 */
if (typeof define === 'function' && define['amd']) {
  define('videojs', [], function () {
    return videojs;
  });

  // checking that module is an object too because of umdjs/umd#35
} else if (typeof exports === 'object' && typeof module === 'object') {
    module['exports'] = videojs;
  }

exports['default'] = videojs;
module.exports = exports['default'];

},{"../../src/js/utils/merge-options.js":150,"./component":66,"./event-target":104,"./extend.js":105,"./player":113,"./plugins.js":114,"./setup":118,"./tech/flash.js":121,"./tech/html5.js":122,"./tech/tech.js":124,"./tracks/audio-track.js":127,"./tracks/text-track.js":135,"./tracks/video-track.js":140,"./utils/browser.js":141,"./utils/create-deprecation-proxy.js":143,"./utils/dom.js":144,"./utils/events.js":145,"./utils/fn.js":146,"./utils/format-time.js":147,"./utils/log.js":149,"./utils/stylesheet.js":151,"./utils/time-ranges.js":152,"./utils/url.js":154,"global/document":7,"global/window":8,"lodash-compat/object/merge":46,"object.assign":53,"xhr":60}]},{},[155])(155)
});



}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{}],391:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _window = _dereq_('global/window');

var _window2 = _interopRequireDefault(_window);

var _video = _dereq_('video.js');

var _video2 = _interopRequireDefault(_video);

var _dashjs = (window.dashjs);

var _dashjs2 = _interopRequireDefault(_dashjs);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var isArray = function isArray(a) {
  return Object.prototype.toString.call(a) === '[object Array]';
};

/**
 * videojs-contrib-dash
 *
 * Use Dash.js to playback DASH content inside of Video.js via a SourceHandler
 */

var Html5DashJS = function () {
  function Html5DashJS(source, tech, options) {
    var _this = this;

    _classCallCheck(this, Html5DashJS);

    // Get options from tech if not provided for backwards compatibility
    options = options || tech.options_;

    this.player = (0, _video2.default)(options.playerId);
    this.player.dash = this.player.dash || {};

    this.tech_ = tech;
    this.el_ = tech.el();
    this.elParent_ = this.el_.parentNode;

    // Do nothing if the src is falsey
    if (!source.src) {
      return;
    }

    // While the manifest is loading and Dash.js has not finished initializing
    // we must defer events and functions calls with isReady_ and then `triggerReady`
    // again later once everything is setup
    tech.isReady_ = false;

    if (Html5DashJS.updateSourceData) {
      _video2.default.log.warn('updateSourceData has been deprecated.' + ' Please switch to using hook("updatesource", callback).');
      source = Html5DashJS.updateSourceData(source);
    }

    // call updatesource hooks
    Html5DashJS.hooks('updatesource').forEach(function (hook) {
      source = hook(source);
    });

    var manifestSource = source.src;
    this.keySystemOptions_ = Html5DashJS.buildDashJSProtData(source.keySystemOptions);

    this.player.dash.mediaPlayer = _dashjs2.default.MediaPlayer().create();

    this.mediaPlayer_ = this.player.dash.mediaPlayer;

    // Log MedaPlayer messages through video.js
    if (Html5DashJS.useVideoJSDebug) {
      _video2.default.log.warn('useVideoJSDebug has been deprecated.' + ' Please switch to using hook("beforeinitialize", callback).');
      Html5DashJS.useVideoJSDebug(this.mediaPlayer_);
    }

    if (Html5DashJS.beforeInitialize) {
      _video2.default.log.warn('beforeInitialize has been deprecated.' + ' Please switch to using hook("beforeinitialize", callback).');
      Html5DashJS.beforeInitialize(this.player, this.mediaPlayer_);
    }

    Html5DashJS.hooks('beforeinitialize').forEach(function (hook) {
      hook(_this.player, _this.mediaPlayer_);
    });

    // Must run controller before these two lines or else there is no
    // element to bind to.
    this.mediaPlayer_.initialize();

    // Apply any options that are set
    if (options.dash && options.dash.limitBitrateByPortal) {
      this.mediaPlayer_.setLimitBitrateByPortal(true);
    } else {
      this.mediaPlayer_.setLimitBitrateByPortal(false);
    }

    this.mediaPlayer_.attachView(this.el_);

    // Dash.js autoplays by default, video.js will handle autoplay
    this.mediaPlayer_.setAutoPlay(false);

    // Attach the source with any protection data
    this.mediaPlayer_.setProtectionData(this.keySystemOptions_);
    this.mediaPlayer_.attachSource(manifestSource);

    this.tech_.triggerReady();
  }

  /*
   * Iterate over the `keySystemOptions` array and convert each object into
   * the type of object Dash.js expects in the `protData` argument.
   *
   * Also rename 'licenseUrl' property in the options to an 'serverURL' property
   */


  _createClass(Html5DashJS, [{
    key: 'dispose',
    value: function dispose() {
      if (this.mediaPlayer_) {
        this.mediaPlayer_.reset();
      }

      if (this.player.dash) {
        delete this.player.dash;
      }
    }
  }, {
    key: 'duration',
    value: function duration() {
      var duration = this.el_.duration;
      if (duration === Number.MAX_VALUE) {
        return Infinity;
      }
      return duration;
    }

    /**
     * Get a list of hooks for a specific lifecycle
     *
     * @param {string} type the lifecycle to get hooks from
     * @param {Function=|Function[]=} hook Optionally add a hook tothe lifecycle
     * @return {Array} an array of hooks or epty if none
     * @method hooks
     */

  }], [{
    key: 'buildDashJSProtData',
    value: function buildDashJSProtData(keySystemOptions) {
      var output = {};

      if (!keySystemOptions || !isArray(keySystemOptions)) {
        return null;
      }

      for (var i = 0; i < keySystemOptions.length; i++) {
        var keySystem = keySystemOptions[i];
        var options = _video2.default.mergeOptions({}, keySystem.options);

        if (options.licenseUrl) {
          options.serverURL = options.licenseUrl;
          delete options.licenseUrl;
        }

        output[keySystem.name] = options;
      }

      return output;
    }
  }, {
    key: 'hooks',
    value: function hooks(type, hook) {
      Html5DashJS.hooks_[type] = Html5DashJS.hooks_[type] || [];

      if (hook) {
        Html5DashJS.hooks_[type] = Html5DashJS.hooks_[type].concat(hook);
      }

      return Html5DashJS.hooks_[type];
    }

    /**
     * Add a function hook to a specific dash lifecycle
     *
     * @param {string} type the lifecycle to hook the function to
     * @param {Function|Function[]} hook the function or array of functions to attach
     * @method hook
     */

  }, {
    key: 'hook',
    value: function hook(type, _hook) {
      Html5DashJS.hooks(type, _hook);
    }

    /**
     * Remove a hook from a specific dash lifecycle.
     *
     * @param {string} type the lifecycle that the function hooked to
     * @param {Function} hook The hooked function to remove
     * @return {boolean} True if the function was removed, false if not found
     * @method removeHook
     */

  }, {
    key: 'removeHook',
    value: function removeHook(type, hook) {
      var index = Html5DashJS.hooks(type).indexOf(hook);

      if (index === -1) {
        return false;
      }

      Html5DashJS.hooks_[type] = Html5DashJS.hooks_[type].slice();
      Html5DashJS.hooks_[type].splice(index, 1);

      return true;
    }
  }]);

  return Html5DashJS;
}();

Html5DashJS.hooks_ = {};

var canHandleKeySystems = function canHandleKeySystems(source) {
  if (Html5DashJS.updateSourceData) {
    source = Html5DashJS.updateSourceData(source);
  }

  var videoEl = document.createElement('video');
  if (source.keySystemOptions && !(navigator.requestMediaKeySystemAccess ||
  // IE11 Win 8.1
  videoEl.msSetMediaKeys)) {
    return false;
  }

  return true;
};

_video2.default.DashSourceHandler = function () {
  return {
    canHandleSource: function canHandleSource(source) {
      var dashExtRE = /\.mpd/i;

      if (!canHandleKeySystems(source)) {
        return '';
      }

      if (_video2.default.DashSourceHandler.canPlayType(source.type)) {
        return 'probably';
      } else if (dashExtRE.test(source.src)) {
        return 'maybe';
      } else {
        return '';
      }
    },

    handleSource: function handleSource(source, tech, options) {
      return new Html5DashJS(source, tech, options);
    },

    canPlayType: function canPlayType(type) {
      return _video2.default.DashSourceHandler.canPlayType(type);
    }
  };
};

_video2.default.DashSourceHandler.canPlayType = function (type) {
  var dashTypeRE = /^application\/dash\+xml/i;
  if (dashTypeRE.test(type)) {
    return 'probably';
  }

  return '';
};

// Only add the SourceHandler if the browser supports MediaSourceExtensions
if (!!_window2.default.MediaSource) {
  _video2.default.getComponent('Html5').registerSourceHandler(_video2.default.DashSourceHandler(), 0);
}

_video2.default.Html5DashJS = Html5DashJS;
exports.default = Html5DashJS;

},{"global/window":177,"video.js":390}],392:[function(_dereq_,module,exports){
/**
 * Copyright 2013 vtt.js Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Default exports for Node. Export the extended versions of VTTCue and
// VTTRegion in Node since we likely want the capability to convert back and
// forth between JSON. If we don't then it's not that big of a deal since we're
// off browser.
var vttjs = module.exports = {
  WebVTT: _dereq_("./vtt.js").WebVTT,
  VTTCue: _dereq_("./vttcue-extended.js").VTTCue,
  VTTRegion: _dereq_("./vttregion-extended.js").VTTRegion
};

window.vttjs = vttjs;
window.WebVTT = vttjs.WebVTT;

var cueShim = vttjs.VTTCue;
var regionShim = vttjs.VTTRegion;
var nativeVTTCue = window.VTTCue;
var nativeVTTRegion = window.VTTRegion;

vttjs.shim = function() {
  window.VTTCue = cueShim;
  window.VTTRegion = regionShim;
};

vttjs.restore = function() {
  window.VTTCue = nativeVTTCue;
  window.VTTRegion = nativeVTTRegion;
};

if (!window.VTTCue) {
  vttjs.shim();
}

},{"./vtt.js":393,"./vttcue-extended.js":394,"./vttregion-extended.js":396}],393:[function(_dereq_,module,exports){
/**
 * Copyright 2013 vtt.js Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

(function(global) {

  var _objCreate = Object.create || (function() {
    function F() {}
    return function(o) {
      if (arguments.length !== 1) {
        throw new Error('Object.create shim only accepts one parameter.');
      }
      F.prototype = o;
      return new F();
    };
  })();

  // Creates a new ParserError object from an errorData object. The errorData
  // object should have default code and message properties. The default message
  // property can be overriden by passing in a message parameter.
  // See ParsingError.Errors below for acceptable errors.
  function ParsingError(errorData, message) {
    this.name = "ParsingError";
    this.code = errorData.code;
    this.message = message || errorData.message;
  }
  ParsingError.prototype = _objCreate(Error.prototype);
  ParsingError.prototype.constructor = ParsingError;

  // ParsingError metadata for acceptable ParsingErrors.
  ParsingError.Errors = {
    BadSignature: {
      code: 0,
      message: "Malformed WebVTT signature."
    },
    BadTimeStamp: {
      code: 1,
      message: "Malformed time stamp."
    }
  };

  // Try to parse input as a time stamp.
  function parseTimeStamp(input) {

    function computeSeconds(h, m, s, f) {
      return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
    }

    var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);
    if (!m) {
      return null;
    }

    if (m[3]) {
      // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
      return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]);
    } else if (m[1] > 59) {
      // Timestamp takes the form of [hours]:[minutes].[milliseconds]
      // First position is hours as it's over 59.
      return computeSeconds(m[1], m[2], 0,  m[4]);
    } else {
      // Timestamp takes the form of [minutes]:[seconds].[milliseconds]
      return computeSeconds(0, m[1], m[2], m[4]);
    }
  }

  // A settings object holds key/value pairs and will ignore anything but the first
  // assignment to a specific key.
  function Settings() {
    this.values = _objCreate(null);
  }

  Settings.prototype = {
    // Only accept the first assignment to any key.
    set: function(k, v) {
      if (!this.get(k) && v !== "") {
        this.values[k] = v;
      }
    },
    // Return the value for a key, or a default value.
    // If 'defaultKey' is passed then 'dflt' is assumed to be an object with
    // a number of possible default values as properties where 'defaultKey' is
    // the key of the property that will be chosen; otherwise it's assumed to be
    // a single value.
    get: function(k, dflt, defaultKey) {
      if (defaultKey) {
        return this.has(k) ? this.values[k] : dflt[defaultKey];
      }
      return this.has(k) ? this.values[k] : dflt;
    },
    // Check whether we have a value for a key.
    has: function(k) {
      return k in this.values;
    },
    // Accept a setting if its one of the given alternatives.
    alt: function(k, v, a) {
      for (var n = 0; n < a.length; ++n) {
        if (v === a[n]) {
          this.set(k, v);
          break;
        }
      }
    },
    // Accept a setting if its a valid (signed) integer.
    integer: function(k, v) {
      if (/^-?\d+$/.test(v)) { // integer
        this.set(k, parseInt(v, 10));
      }
    },
    // Accept a setting if its a valid percentage.
    percent: function(k, v) {
      var m;
      if ((m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/))) {
        v = parseFloat(v);
        if (v >= 0 && v <= 100) {
          this.set(k, v);
          return true;
        }
      }
      return false;
    }
  };

  // Helper function to parse input into groups separated by 'groupDelim', and
  // interprete each group as a key/value pair separated by 'keyValueDelim'.
  function parseOptions(input, callback, keyValueDelim, groupDelim) {
    var groups = groupDelim ? input.split(groupDelim) : [input];
    for (var i in groups) {
      if (typeof groups[i] !== "string") {
        continue;
      }
      var kv = groups[i].split(keyValueDelim);
      if (kv.length !== 2) {
        continue;
      }
      var k = kv[0];
      var v = kv[1];
      callback(k, v);
    }
  }

  function parseCue(input, cue, regionList) {
    // Remember the original input if we need to throw an error.
    var oInput = input;
    // 4.1 WebVTT timestamp
    function consumeTimeStamp() {
      var ts = parseTimeStamp(input);
      if (ts === null) {
        throw new ParsingError(ParsingError.Errors.BadTimeStamp,
                              "Malformed timestamp: " + oInput);
      }
      // Remove time stamp from input.
      input = input.replace(/^[^\sa-zA-Z-]+/, "");
      return ts;
    }

    // 4.4.2 WebVTT cue settings
    function consumeCueSettings(input, cue) {
      var settings = new Settings();

      parseOptions(input, function (k, v) {
        switch (k) {
        case "region":
          // Find the last region we parsed with the same region id.
          for (var i = regionList.length - 1; i >= 0; i--) {
            if (regionList[i].id === v) {
              settings.set(k, regionList[i].region);
              break;
            }
          }
          break;
        case "vertical":
          settings.alt(k, v, ["rl", "lr"]);
          break;
        case "line":
          var vals = v.split(","),
              vals0 = vals[0];
          settings.integer(k, vals0);
          settings.percent(k, vals0) ? settings.set("snapToLines", false) : null;
          settings.alt(k, vals0, ["auto"]);
          if (vals.length === 2) {
            settings.alt("lineAlign", vals[1], ["start", "middle", "end"]);
          }
          break;
        case "position":
          vals = v.split(",");
          settings.percent(k, vals[0]);
          if (vals.length === 2) {
            settings.alt("positionAlign", vals[1], ["start", "middle", "end"]);
          }
          break;
        case "size":
          settings.percent(k, v);
          break;
        case "align":
          settings.alt(k, v, ["start", "middle", "end", "left", "right"]);
          break;
        }
      }, /:/, /\s/);

      // Apply default values for any missing fields.
      cue.region = settings.get("region", null);
      cue.vertical = settings.get("vertical", "");
      cue.line = settings.get("line", "auto");
      cue.lineAlign = settings.get("lineAlign", "start");
      cue.snapToLines = settings.get("snapToLines", true);
      cue.size = settings.get("size", 100);
      cue.align = settings.get("align", "middle");
      cue.position = settings.get("position", {
        start: 0,
        left: 0,
        middle: 50,
        end: 100,
        right: 100
      }, cue.align);
      cue.positionAlign = settings.get("positionAlign", {
        start: "start",
        left: "start",
        middle: "middle",
        end: "end",
        right: "end"
      }, cue.align);
    }

    function skipWhitespace() {
      input = input.replace(/^\s+/, "");
    }

    // 4.1 WebVTT cue timings.
    skipWhitespace();
    cue.startTime = consumeTimeStamp();   // (1) collect cue start time
    skipWhitespace();
    if (input.substr(0, 3) !== "-->") {     // (3) next characters must match "-->"
      throw new ParsingError(ParsingError.Errors.BadTimeStamp,
                             "Malformed time stamp (time stamps must be separated by '-->'): " +
                             oInput);
    }
    input = input.substr(3);
    skipWhitespace();
    cue.endTime = consumeTimeStamp();     // (5) collect cue end time

    // 4.1 WebVTT cue settings list.
    skipWhitespace();
    consumeCueSettings(input, cue);
  }

  var ESCAPE = {
    "&amp;": "&",
    "&lt;": "<",
    "&gt;": ">",
    "&lrm;": "\u200e",
    "&rlm;": "\u200f",
    "&nbsp;": "\u00a0"
  };

  var TAG_NAME = {
    c: "span",
    i: "i",
    b: "b",
    u: "u",
    ruby: "ruby",
    rt: "rt",
    v: "span",
    lang: "span"
  };

  var TAG_ANNOTATION = {
    v: "title",
    lang: "lang"
  };

  var NEEDS_PARENT = {
    rt: "ruby"
  };

  // Parse content into a document fragment.
  function parseContent(window, input) {
    function nextToken() {
      // Check for end-of-string.
      if (!input) {
        return null;
      }

      // Consume 'n' characters from the input.
      function consume(result) {
        input = input.substr(result.length);
        return result;
      }

      var m = input.match(/^([^<]*)(<[^>]+>?)?/);
      // If there is some text before the next tag, return it, otherwise return
      // the tag.
      return consume(m[1] ? m[1] : m[2]);
    }

    // Unescape a string 's'.
    function unescape1(e) {
      return ESCAPE[e];
    }
    function unescape(s) {
      while ((m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/))) {
        s = s.replace(m[0], unescape1);
      }
      return s;
    }

    function shouldAdd(current, element) {
      return !NEEDS_PARENT[element.localName] ||
             NEEDS_PARENT[element.localName] === current.localName;
    }

    // Create an element for this tag.
    function createElement(type, annotation) {
      var tagName = TAG_NAME[type];
      if (!tagName) {
        return null;
      }
      var element = window.document.createElement(tagName);
      element.localName = tagName;
      var name = TAG_ANNOTATION[type];
      if (name && annotation) {
        element[name] = annotation.trim();
      }
      return element;
    }

    var rootDiv = window.document.createElement("div"),
        current = rootDiv,
        t,
        tagStack = [];

    while ((t = nextToken()) !== null) {
      if (t[0] === '<') {
        if (t[1] === "/") {
          // If the closing tag matches, move back up to the parent node.
          if (tagStack.length &&
              tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) {
            tagStack.pop();
            current = current.parentNode;
          }
          // Otherwise just ignore the end tag.
          continue;
        }
        var ts = parseTimeStamp(t.substr(1, t.length - 2));
        var node;
        if (ts) {
          // Timestamps are lead nodes as well.
          node = window.document.createProcessingInstruction("timestamp", ts);
          current.appendChild(node);
          continue;
        }
        var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/);
        // If we can't parse the tag, skip to the next tag.
        if (!m) {
          continue;
        }
        // Try to construct an element, and ignore the tag if we couldn't.
        node = createElement(m[1], m[3]);
        if (!node) {
          continue;
        }
        // Determine if the tag should be added based on the context of where it
        // is placed in the cuetext.
        if (!shouldAdd(current, node)) {
          continue;
        }
        // Set the class list (as a list of classes, separated by space).
        if (m[2]) {
          node.className = m[2].substr(1).replace('.', ' ');
        }
        // Append the node to the current node, and enter the scope of the new
        // node.
        tagStack.push(m[1]);
        current.appendChild(node);
        current = node;
        continue;
      }

      // Text nodes are leaf nodes.
      current.appendChild(window.document.createTextNode(unescape(t)));
    }

    return rootDiv;
  }

  // This is a list of all the Unicode characters that have a strong
  // right-to-left category. What this means is that these characters are
  // written right-to-left for sure. It was generated by pulling all the strong
  // right-to-left characters out of the Unicode data table. That table can
  // found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
  var strongRTLRanges = [[0x5be, 0x5be], [0x5c0, 0x5c0], [0x5c3, 0x5c3], [0x5c6, 0x5c6],
   [0x5d0, 0x5ea], [0x5f0, 0x5f4], [0x608, 0x608], [0x60b, 0x60b], [0x60d, 0x60d],
   [0x61b, 0x61b], [0x61e, 0x64a], [0x66d, 0x66f], [0x671, 0x6d5], [0x6e5, 0x6e6],
   [0x6ee, 0x6ef], [0x6fa, 0x70d], [0x70f, 0x710], [0x712, 0x72f], [0x74d, 0x7a5],
   [0x7b1, 0x7b1], [0x7c0, 0x7ea], [0x7f4, 0x7f5], [0x7fa, 0x7fa], [0x800, 0x815],
   [0x81a, 0x81a], [0x824, 0x824], [0x828, 0x828], [0x830, 0x83e], [0x840, 0x858],
   [0x85e, 0x85e], [0x8a0, 0x8a0], [0x8a2, 0x8ac], [0x200f, 0x200f],
   [0xfb1d, 0xfb1d], [0xfb1f, 0xfb28], [0xfb2a, 0xfb36], [0xfb38, 0xfb3c],
   [0xfb3e, 0xfb3e], [0xfb40, 0xfb41], [0xfb43, 0xfb44], [0xfb46, 0xfbc1],
   [0xfbd3, 0xfd3d], [0xfd50, 0xfd8f], [0xfd92, 0xfdc7], [0xfdf0, 0xfdfc],
   [0xfe70, 0xfe74], [0xfe76, 0xfefc], [0x10800, 0x10805], [0x10808, 0x10808],
   [0x1080a, 0x10835], [0x10837, 0x10838], [0x1083c, 0x1083c], [0x1083f, 0x10855],
   [0x10857, 0x1085f], [0x10900, 0x1091b], [0x10920, 0x10939], [0x1093f, 0x1093f],
   [0x10980, 0x109b7], [0x109be, 0x109bf], [0x10a00, 0x10a00], [0x10a10, 0x10a13],
   [0x10a15, 0x10a17], [0x10a19, 0x10a33], [0x10a40, 0x10a47], [0x10a50, 0x10a58],
   [0x10a60, 0x10a7f], [0x10b00, 0x10b35], [0x10b40, 0x10b55], [0x10b58, 0x10b72],
   [0x10b78, 0x10b7f], [0x10c00, 0x10c48], [0x1ee00, 0x1ee03], [0x1ee05, 0x1ee1f],
   [0x1ee21, 0x1ee22], [0x1ee24, 0x1ee24], [0x1ee27, 0x1ee27], [0x1ee29, 0x1ee32],
   [0x1ee34, 0x1ee37], [0x1ee39, 0x1ee39], [0x1ee3b, 0x1ee3b], [0x1ee42, 0x1ee42],
   [0x1ee47, 0x1ee47], [0x1ee49, 0x1ee49], [0x1ee4b, 0x1ee4b], [0x1ee4d, 0x1ee4f],
   [0x1ee51, 0x1ee52], [0x1ee54, 0x1ee54], [0x1ee57, 0x1ee57], [0x1ee59, 0x1ee59],
   [0x1ee5b, 0x1ee5b], [0x1ee5d, 0x1ee5d], [0x1ee5f, 0x1ee5f], [0x1ee61, 0x1ee62],
   [0x1ee64, 0x1ee64], [0x1ee67, 0x1ee6a], [0x1ee6c, 0x1ee72], [0x1ee74, 0x1ee77],
   [0x1ee79, 0x1ee7c], [0x1ee7e, 0x1ee7e], [0x1ee80, 0x1ee89], [0x1ee8b, 0x1ee9b],
   [0x1eea1, 0x1eea3], [0x1eea5, 0x1eea9], [0x1eeab, 0x1eebb], [0x10fffd, 0x10fffd]];

  function isStrongRTLChar(charCode) {
    for (var i = 0; i < strongRTLRanges.length; i++) {
      var currentRange = strongRTLRanges[i];
      if (charCode >= currentRange[0] && charCode <= currentRange[1]) {
        return true;
      }
    }

    return false;
  }

  function determineBidi(cueDiv) {
    var nodeStack = [],
        text = "",
        charCode;

    if (!cueDiv || !cueDiv.childNodes) {
      return "ltr";
    }

    function pushNodes(nodeStack, node) {
      for (var i = node.childNodes.length - 1; i >= 0; i--) {
        nodeStack.push(node.childNodes[i]);
      }
    }

    function nextTextNode(nodeStack) {
      if (!nodeStack || !nodeStack.length) {
        return null;
      }

      var node = nodeStack.pop(),
          text = node.textContent || node.innerText;
      if (text) {
        // TODO: This should match all unicode type B characters (paragraph
        // separator characters). See issue #115.
        var m = text.match(/^.*(\n|\r)/);
        if (m) {
          nodeStack.length = 0;
          return m[0];
        }
        return text;
      }
      if (node.tagName === "ruby") {
        return nextTextNode(nodeStack);
      }
      if (node.childNodes) {
        pushNodes(nodeStack, node);
        return nextTextNode(nodeStack);
      }
    }

    pushNodes(nodeStack, cueDiv);
    while ((text = nextTextNode(nodeStack))) {
      for (var i = 0; i < text.length; i++) {
        charCode = text.charCodeAt(i);
        if (isStrongRTLChar(charCode)) {
          return "rtl";
        }
      }
    }
    return "ltr";
  }

  function computeLinePos(cue) {
    if (typeof cue.line === "number" &&
        (cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) {
      return cue.line;
    }
    if (!cue.track || !cue.track.textTrackList ||
        !cue.track.textTrackList.mediaElement) {
      return -1;
    }
    var track = cue.track,
        trackList = track.textTrackList,
        count = 0;
    for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
      if (trackList[i].mode === "showing") {
        count++;
      }
    }
    return ++count * -1;
  }

  function StyleBox() {
  }

  // Apply styles to a div. If there is no div passed then it defaults to the
  // div on 'this'.
  StyleBox.prototype.applyStyles = function(styles, div) {
    div = div || this.div;
    for (var prop in styles) {
      if (styles.hasOwnProperty(prop)) {
        div.style[prop] = styles[prop];
      }
    }
  };

  StyleBox.prototype.formatStyle = function(val, unit) {
    return val === 0 ? 0 : val + unit;
  };

  // Constructs the computed display state of the cue (a div). Places the div
  // into the overlay which should be a block level element (usually a div).
  function CueStyleBox(window, cue, styleOptions) {
    var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
    var color = "rgba(255, 255, 255, 1)";
    var backgroundColor = "rgba(0, 0, 0, 0.8)";

    if (isIE8) {
      color = "rgb(255, 255, 255)";
      backgroundColor = "rgb(0, 0, 0)";
    }

    StyleBox.call(this);
    this.cue = cue;

    // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will
    // have inline positioning and will function as the cue background box.
    this.cueDiv = parseContent(window, cue.text);
    var styles = {
      color: color,
      backgroundColor: backgroundColor,
      position: "relative",
      left: 0,
      right: 0,
      top: 0,
      bottom: 0,
      display: "inline"
    };

    if (!isIE8) {
      styles.writingMode = cue.vertical === "" ? "horizontal-tb"
                                               : cue.vertical === "lr" ? "vertical-lr"
                                                                       : "vertical-rl";
      styles.unicodeBidi = "plaintext";
    }
    this.applyStyles(styles, this.cueDiv);

    // Create an absolutely positioned div that will be used to position the cue
    // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS
    // mirrors of them except "middle" which is "center" in CSS.
    this.div = window.document.createElement("div");
    styles = {
      textAlign: cue.align === "middle" ? "center" : cue.align,
      font: styleOptions.font,
      whiteSpace: "pre-line",
      position: "absolute"
    };

    if (!isIE8) {
      styles.direction = determineBidi(this.cueDiv);
      styles.writingMode = cue.vertical === "" ? "horizontal-tb"
                                               : cue.vertical === "lr" ? "vertical-lr"
                                                                       : "vertical-rl".
      stylesunicodeBidi =  "plaintext";
    }

    this.applyStyles(styles);

    this.div.appendChild(this.cueDiv);

    // Calculate the distance from the reference edge of the viewport to the text
    // position of the cue box. The reference edge will be resolved later when
    // the box orientation styles are applied.
    var textPos = 0;
    switch (cue.positionAlign) {
    case "start":
      textPos = cue.position;
      break;
    case "middle":
      textPos = cue.position - (cue.size / 2);
      break;
    case "end":
      textPos = cue.position - cue.size;
      break;
    }

    // Horizontal box orientation; textPos is the distance from the left edge of the
    // area to the left edge of the box and cue.size is the distance extending to
    // the right from there.
    if (cue.vertical === "") {
      this.applyStyles({
        left:  this.formatStyle(textPos, "%"),
        width: this.formatStyle(cue.size, "%")
      });
    // Vertical box orientation; textPos is the distance from the top edge of the
    // area to the top edge of the box and cue.size is the height extending
    // downwards from there.
    } else {
      this.applyStyles({
        top: this.formatStyle(textPos, "%"),
        height: this.formatStyle(cue.size, "%")
      });
    }

    this.move = function(box) {
      this.applyStyles({
        top: this.formatStyle(box.top, "px"),
        bottom: this.formatStyle(box.bottom, "px"),
        left: this.formatStyle(box.left, "px"),
        right: this.formatStyle(box.right, "px"),
        height: this.formatStyle(box.height, "px"),
        width: this.formatStyle(box.width, "px")
      });
    };
  }
  CueStyleBox.prototype = _objCreate(StyleBox.prototype);
  CueStyleBox.prototype.constructor = CueStyleBox;

  // Represents the co-ordinates of an Element in a way that we can easily
  // compute things with such as if it overlaps or intersects with another Element.
  // Can initialize it with either a StyleBox or another BoxPosition.
  function BoxPosition(obj) {
    var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);

    // Either a BoxPosition was passed in and we need to copy it, or a StyleBox
    // was passed in and we need to copy the results of 'getBoundingClientRect'
    // as the object returned is readonly. All co-ordinate values are in reference
    // to the viewport origin (top left).
    var lh, height, width, top;
    if (obj.div) {
      height = obj.div.offsetHeight;
      width = obj.div.offsetWidth;
      top = obj.div.offsetTop;

      var rects = (rects = obj.div.childNodes) && (rects = rects[0]) &&
                  rects.getClientRects && rects.getClientRects();
      obj = obj.div.getBoundingClientRect();
      // In certain cases the outter div will be slightly larger then the sum of
      // the inner div's lines. This could be due to bold text, etc, on some platforms.
      // In this case we should get the average line height and use that. This will
      // result in the desired behaviour.
      lh = rects ? Math.max((rects[0] && rects[0].height) || 0, obj.height / rects.length)
                 : 0;

    }
    this.left = obj.left;
    this.right = obj.right;
    this.top = obj.top || top;
    this.height = obj.height || height;
    this.bottom = obj.bottom || (top + (obj.height || height));
    this.width = obj.width || width;
    this.lineHeight = lh !== undefined ? lh : obj.lineHeight;

    if (isIE8 && !this.lineHeight) {
      this.lineHeight = 13;
    }
  }

  // Move the box along a particular axis. Optionally pass in an amount to move
  // the box. If no amount is passed then the default is the line height of the
  // box.
  BoxPosition.prototype.move = function(axis, toMove) {
    toMove = toMove !== undefined ? toMove : this.lineHeight;
    switch (axis) {
    case "+x":
      this.left += toMove;
      this.right += toMove;
      break;
    case "-x":
      this.left -= toMove;
      this.right -= toMove;
      break;
    case "+y":
      this.top += toMove;
      this.bottom += toMove;
      break;
    case "-y":
      this.top -= toMove;
      this.bottom -= toMove;
      break;
    }
  };

  // Check if this box overlaps another box, b2.
  BoxPosition.prototype.overlaps = function(b2) {
    return this.left < b2.right &&
           this.right > b2.left &&
           this.top < b2.bottom &&
           this.bottom > b2.top;
  };

  // Check if this box overlaps any other boxes in boxes.
  BoxPosition.prototype.overlapsAny = function(boxes) {
    for (var i = 0; i < boxes.length; i++) {
      if (this.overlaps(boxes[i])) {
        return true;
      }
    }
    return false;
  };

  // Check if this box is within another box.
  BoxPosition.prototype.within = function(container) {
    return this.top >= container.top &&
           this.bottom <= container.bottom &&
           this.left >= container.left &&
           this.right <= container.right;
  };

  // Check if this box is entirely within the container or it is overlapping
  // on the edge opposite of the axis direction passed. For example, if "+x" is
  // passed and the box is overlapping on the left edge of the container, then
  // return true.
  BoxPosition.prototype.overlapsOppositeAxis = function(container, axis) {
    switch (axis) {
    case "+x":
      return this.left < container.left;
    case "-x":
      return this.right > container.right;
    case "+y":
      return this.top < container.top;
    case "-y":
      return this.bottom > container.bottom;
    }
  };

  // Find the percentage of the area that this box is overlapping with another
  // box.
  BoxPosition.prototype.intersectPercentage = function(b2) {
    var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)),
        y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)),
        intersectArea = x * y;
    return intersectArea / (this.height * this.width);
  };

  // Convert the positions from this box to CSS compatible positions using
  // the reference container's positions. This has to be done because this
  // box's positions are in reference to the viewport origin, whereas, CSS
  // values are in referecne to their respective edges.
  BoxPosition.prototype.toCSSCompatValues = function(reference) {
    return {
      top: this.top - reference.top,
      bottom: reference.bottom - this.bottom,
      left: this.left - reference.left,
      right: reference.right - this.right,
      height: this.height,
      width: this.width
    };
  };

  // Get an object that represents the box's position without anything extra.
  // Can pass a StyleBox, HTMLElement, or another BoxPositon.
  BoxPosition.getSimpleBoxPosition = function(obj) {
    var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0;
    var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0;
    var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0;

    obj = obj.div ? obj.div.getBoundingClientRect() :
                  obj.tagName ? obj.getBoundingClientRect() : obj;
    var ret = {
      left: obj.left,
      right: obj.right,
      top: obj.top || top,
      height: obj.height || height,
      bottom: obj.bottom || (top + (obj.height || height)),
      width: obj.width || width
    };
    return ret;
  };

  // Move a StyleBox to its specified, or next best, position. The containerBox
  // is the box that contains the StyleBox, such as a div. boxPositions are
  // a list of other boxes that the styleBox can't overlap with.
  function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) {

    // Find the best position for a cue box, b, on the video. The axis parameter
    // is a list of axis, the order of which, it will move the box along. For example:
    // Passing ["+x", "-x"] will move the box first along the x axis in the positive
    // direction. If it doesn't find a good position for it there it will then move
    // it along the x axis in the negative direction.
    function findBestPosition(b, axis) {
      var bestPosition,
          specifiedPosition = new BoxPosition(b),
          percentage = 1; // Highest possible so the first thing we get is better.

      for (var i = 0; i < axis.length; i++) {
        while (b.overlapsOppositeAxis(containerBox, axis[i]) ||
               (b.within(containerBox) && b.overlapsAny(boxPositions))) {
          b.move(axis[i]);
        }
        // We found a spot where we aren't overlapping anything. This is our
        // best position.
        if (b.within(containerBox)) {
          return b;
        }
        var p = b.intersectPercentage(containerBox);
        // If we're outside the container box less then we were on our last try
        // then remember this position as the best position.
        if (percentage > p) {
          bestPosition = new BoxPosition(b);
          percentage = p;
        }
        // Reset the box position to the specified position.
        b = new BoxPosition(specifiedPosition);
      }
      return bestPosition || specifiedPosition;
    }

    var boxPosition = new BoxPosition(styleBox),
        cue = styleBox.cue,
        linePos = computeLinePos(cue),
        axis = [];

    // If we have a line number to align the cue to.
    if (cue.snapToLines) {
      var size;
      switch (cue.vertical) {
      case "":
        axis = [ "+y", "-y" ];
        size = "height";
        break;
      case "rl":
        axis = [ "+x", "-x" ];
        size = "width";
        break;
      case "lr":
        axis = [ "-x", "+x" ];
        size = "width";
        break;
      }

      var step = boxPosition.lineHeight,
          position = step * Math.round(linePos),
          maxPosition = containerBox[size] + step,
          initialAxis = axis[0];

      // If the specified intial position is greater then the max position then
      // clamp the box to the amount of steps it would take for the box to
      // reach the max position.
      if (Math.abs(position) > maxPosition) {
        position = position < 0 ? -1 : 1;
        position *= Math.ceil(maxPosition / step) * step;
      }

      // If computed line position returns negative then line numbers are
      // relative to the bottom of the video instead of the top. Therefore, we
      // need to increase our initial position by the length or width of the
      // video, depending on the writing direction, and reverse our axis directions.
      if (linePos < 0) {
        position += cue.vertical === "" ? containerBox.height : containerBox.width;
        axis = axis.reverse();
      }

      // Move the box to the specified position. This may not be its best
      // position.
      boxPosition.move(initialAxis, position);

    } else {
      // If we have a percentage line value for the cue.
      var calculatedPercentage = (boxPosition.lineHeight / containerBox.height) * 100;

      switch (cue.lineAlign) {
      case "middle":
        linePos -= (calculatedPercentage / 2);
        break;
      case "end":
        linePos -= calculatedPercentage;
        break;
      }

      // Apply initial line position to the cue box.
      switch (cue.vertical) {
      case "":
        styleBox.applyStyles({
          top: styleBox.formatStyle(linePos, "%")
        });
        break;
      case "rl":
        styleBox.applyStyles({
          left: styleBox.formatStyle(linePos, "%")
        });
        break;
      case "lr":
        styleBox.applyStyles({
          right: styleBox.formatStyle(linePos, "%")
        });
        break;
      }

      axis = [ "+y", "-x", "+x", "-y" ];

      // Get the box position again after we've applied the specified positioning
      // to it.
      boxPosition = new BoxPosition(styleBox);
    }

    var bestPosition = findBestPosition(boxPosition, axis);
    styleBox.move(bestPosition.toCSSCompatValues(containerBox));
  }

  function WebVTT() {
    // Nothing
  }

  // Helper to allow strings to be decoded instead of the default binary utf8 data.
  WebVTT.StringDecoder = function() {
    return {
      decode: function(data) {
        if (!data) {
          return "";
        }
        if (typeof data !== "string") {
          throw new Error("Error - expected string data.");
        }
        return decodeURIComponent(encodeURIComponent(data));
      }
    };
  };

  WebVTT.convertCueToDOMTree = function(window, cuetext) {
    if (!window || !cuetext) {
      return null;
    }
    return parseContent(window, cuetext);
  };

  var FONT_SIZE_PERCENT = 0.05;
  var FONT_STYLE = "sans-serif";
  var CUE_BACKGROUND_PADDING = "1.5%";

  // Runs the processing model over the cues and regions passed to it.
  // @param overlay A block level element (usually a div) that the computed cues
  //                and regions will be placed into.
  WebVTT.processCues = function(window, cues, overlay) {
    if (!window || !cues || !overlay) {
      return null;
    }

    // Remove all previous children.
    while (overlay.firstChild) {
      overlay.removeChild(overlay.firstChild);
    }

    var paddedOverlay = window.document.createElement("div");
    paddedOverlay.style.position = "absolute";
    paddedOverlay.style.left = "0";
    paddedOverlay.style.right = "0";
    paddedOverlay.style.top = "0";
    paddedOverlay.style.bottom = "0";
    paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;
    overlay.appendChild(paddedOverlay);

    // Determine if we need to compute the display states of the cues. This could
    // be the case if a cue's state has been changed since the last computation or
    // if it has not been computed yet.
    function shouldCompute(cues) {
      for (var i = 0; i < cues.length; i++) {
        if (cues[i].hasBeenReset || !cues[i].displayState) {
          return true;
        }
      }
      return false;
    }

    // We don't need to recompute the cues' display states. Just reuse them.
    if (!shouldCompute(cues)) {
      for (var i = 0; i < cues.length; i++) {
        paddedOverlay.appendChild(cues[i].displayState);
      }
      return;
    }

    var boxPositions = [],
        containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay),
        fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100;
    var styleOptions = {
      font: fontSize + "px " + FONT_STYLE
    };

    (function() {
      var styleBox, cue;

      for (var i = 0; i < cues.length; i++) {
        cue = cues[i];

        // Compute the intial position and styles of the cue div.
        styleBox = new CueStyleBox(window, cue, styleOptions);
        paddedOverlay.appendChild(styleBox.div);

        // Move the cue div to it's correct line position.
        moveBoxToLinePosition(window, styleBox, containerBox, boxPositions);

        // Remember the computed div so that we don't have to recompute it later
        // if we don't have too.
        cue.displayState = styleBox.div;

        boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
      }
    })();
  };

  WebVTT.Parser = function(window, vttjs, decoder) {
    if (!decoder) {
      decoder = vttjs;
      vttjs = {};
    }
    if (!vttjs) {
      vttjs = {};
    }

    this.window = window;
    this.vttjs = vttjs;
    this.state = "INITIAL";
    this.buffer = "";
    this.decoder = decoder || new TextDecoder("utf8");
    this.regionList = [];
  };

  WebVTT.Parser.prototype = {
    // If the error is a ParsingError then report it to the consumer if
    // possible. If it's not a ParsingError then throw it like normal.
    reportOrThrowError: function(e) {
      if (e instanceof ParsingError) {
        this.onparsingerror && this.onparsingerror(e);
      } else {
        throw e;
      }
    },
    parse: function (data) {
      var self = this;

      // If there is no data then we won't decode it, but will just try to parse
      // whatever is in buffer already. This may occur in circumstances, for
      // example when flush() is called.
      if (data) {
        // Try to decode the data that we received.
        self.buffer += self.decoder.decode(data, {stream: true});
      }

      function collectNextLine() {
        var buffer = self.buffer;
        var pos = 0;
        while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
          ++pos;
        }
        var line = buffer.substr(0, pos);
        // Advance the buffer early in case we fail below.
        if (buffer[pos] === '\r') {
          ++pos;
        }
        if (buffer[pos] === '\n') {
          ++pos;
        }
        self.buffer = buffer.substr(pos);
        return line;
      }

      // 3.4 WebVTT region and WebVTT region settings syntax
      function parseRegion(input) {
        var settings = new Settings();

        parseOptions(input, function (k, v) {
          switch (k) {
          case "id":
            settings.set(k, v);
            break;
          case "width":
            settings.percent(k, v);
            break;
          case "lines":
            settings.integer(k, v);
            break;
          case "regionanchor":
          case "viewportanchor":
            var xy = v.split(',');
            if (xy.length !== 2) {
              break;
            }
            // We have to make sure both x and y parse, so use a temporary
            // settings object here.
            var anchor = new Settings();
            anchor.percent("x", xy[0]);
            anchor.percent("y", xy[1]);
            if (!anchor.has("x") || !anchor.has("y")) {
              break;
            }
            settings.set(k + "X", anchor.get("x"));
            settings.set(k + "Y", anchor.get("y"));
            break;
          case "scroll":
            settings.alt(k, v, ["up"]);
            break;
          }
        }, /=/, /\s/);

        // Create the region, using default values for any values that were not
        // specified.
        if (settings.has("id")) {
          var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)();
          region.width = settings.get("width", 100);
          region.lines = settings.get("lines", 3);
          region.regionAnchorX = settings.get("regionanchorX", 0);
          region.regionAnchorY = settings.get("regionanchorY", 100);
          region.viewportAnchorX = settings.get("viewportanchorX", 0);
          region.viewportAnchorY = settings.get("viewportanchorY", 100);
          region.scroll = settings.get("scroll", "");
          // Register the region.
          self.onregion && self.onregion(region);
          // Remember the VTTRegion for later in case we parse any VTTCues that
          // reference it.
          self.regionList.push({
            id: settings.get("id"),
            region: region
          });
        }
      }

      // draft-pantos-http-live-streaming-20
      // https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-3.5
      // 3.5 WebVTT
      function parseTimestampMap(input) {
        var settings = new Settings();

        parseOptions(input, function(k, v) {
          switch(k) {
          case "MPEGT":
            settings.integer(k + 'S', v);
            break;
          case "LOCA":
            settings.set(k + 'L', parseTimeStamp(v));
            break;
          }
        }, /[^\d]:/, /,/);

        self.ontimestampmap && self.ontimestampmap({
          "MPEGTS": settings.get("MPEGTS"),
          "LOCAL": settings.get("LOCAL")
        });
      }

      // 3.2 WebVTT metadata header syntax
      function parseHeader(input) {
        if (input.match(/X-TIMESTAMP-MAP/)) {
          // This line contains HLS X-TIMESTAMP-MAP metadata
          parseOptions(input, function(k, v) {
            switch(k) {
            case "X-TIMESTAMP-MAP":
              parseTimestampMap(v);
              break;
            }
          }, /=/);
        } else {
          parseOptions(input, function (k, v) {
            switch (k) {
            case "Region":
              // 3.3 WebVTT region metadata header syntax
              parseRegion(v);
              break;
            }
          }, /:/);
        }

      }

      // 5.1 WebVTT file parsing.
      try {
        var line;
        if (self.state === "INITIAL") {
          // We can't start parsing until we have the first line.
          if (!/\r\n|\n/.test(self.buffer)) {
            return this;
          }

          line = collectNextLine();

          var m = line.match(/^WEBVTT([ \t].*)?$/);
          if (!m || !m[0]) {
            throw new ParsingError(ParsingError.Errors.BadSignature);
          }

          self.state = "HEADER";
        }

        var alreadyCollectedLine = false;
        while (self.buffer) {
          // We can't parse a line until we have the full line.
          if (!/\r\n|\n/.test(self.buffer)) {
            return this;
          }

          if (!alreadyCollectedLine) {
            line = collectNextLine();
          } else {
            alreadyCollectedLine = false;
          }

          switch (self.state) {
          case "HEADER":
            // 13-18 - Allow a header (metadata) under the WEBVTT line.
            if (/:/.test(line)) {
              parseHeader(line);
            } else if (!line) {
              // An empty line terminates the header and starts the body (cues).
              self.state = "ID";
            }
            continue;
          case "NOTE":
            // Ignore NOTE blocks.
            if (!line) {
              self.state = "ID";
            }
            continue;
          case "ID":
            // Check for the start of NOTE blocks.
            if (/^NOTE($|[ \t])/.test(line)) {
              self.state = "NOTE";
              break;
            }
            // 19-29 - Allow any number of line terminators, then initialize new cue values.
            if (!line) {
              continue;
            }
            self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, "");
            self.state = "CUE";
            // 30-39 - Check if self line contains an optional identifier or timing data.
            if (line.indexOf("-->") === -1) {
              self.cue.id = line;
              continue;
            }
            // Process line as start of a cue.
            /*falls through*/
          case "CUE":
            // 40 - Collect cue timings and settings.
            try {
              parseCue(line, self.cue, self.regionList);
            } catch (e) {
              self.reportOrThrowError(e);
              // In case of an error ignore rest of the cue.
              self.cue = null;
              self.state = "BADCUE";
              continue;
            }
            self.state = "CUETEXT";
            continue;
          case "CUETEXT":
            var hasSubstring = line.indexOf("-->") !== -1;
            // 34 - If we have an empty line then report the cue.
            // 35 - If we have the special substring '-->' then report the cue,
            // but do not collect the line as we need to process the current
            // one as a new cue.
            if (!line || hasSubstring && (alreadyCollectedLine = true)) {
              // We are done parsing self cue.
              self.oncue && self.oncue(self.cue);
              self.cue = null;
              self.state = "ID";
              continue;
            }
            if (self.cue.text) {
              self.cue.text += "\n";
            }
            self.cue.text += line;
            continue;
          case "BADCUE": // BADCUE
            // 54-62 - Collect and discard the remaining cue.
            if (!line) {
              self.state = "ID";
            }
            continue;
          }
        }
      } catch (e) {
        self.reportOrThrowError(e);

        // If we are currently parsing a cue, report what we have.
        if (self.state === "CUETEXT" && self.cue && self.oncue) {
          self.oncue(self.cue);
        }
        self.cue = null;
        // Enter BADWEBVTT state if header was not parsed correctly otherwise
        // another exception occurred so enter BADCUE state.
        self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE";
      }
      return this;
    },
    flush: function () {
      var self = this;
      try {
        // Finish decoding the stream.
        self.buffer += self.decoder.decode();
        // Synthesize the end of the current cue or region.
        if (self.cue || self.state === "HEADER") {
          self.buffer += "\n\n";
          self.parse();
        }
        // If we've flushed, parsed, and we're still on the INITIAL state then
        // that means we don't have enough of the stream to parse the first
        // line.
        if (self.state === "INITIAL") {
          throw new ParsingError(ParsingError.Errors.BadSignature);
        }
      } catch(e) {
        self.reportOrThrowError(e);
      }
      self.onflush && self.onflush();
      return this;
    }
  };

  global.WebVTT = WebVTT;

}(this, (this.vttjs || {})));

},{}],394:[function(_dereq_,module,exports){
/**
 * Copyright 2013 vtt.js Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// If we're in Node.js then require VTTCue so we can extend it, otherwise assume
// VTTCue is on the global.
if (typeof module !== "undefined" && module.exports) {
  this.VTTCue = this.VTTCue || _dereq_("./vttcue").VTTCue;
}

// Extend VTTCue with methods to convert to JSON, from JSON, and construct a
// VTTCue from an options object. The primary purpose of this is for use in the
// vtt.js test suite (for testing only properties that we care about). It's also
// useful if you need to work with VTTCues in JSON format.
(function(root) {

  root.VTTCue.prototype.toJSON = function() {
    var cue = {},
        self = this;
    // Filter out getCueAsHTML as it's a function and hasBeenReset and displayState as
    // they're only used when running the processing model algorithm.
    Object.keys(this).forEach(function(key) {
      if (key !== "getCueAsHTML" && key !== "hasBeenReset" && key !== "displayState") {
        cue[key] = self[key];
      }
    });
    return cue;
  };

  root.VTTCue.create = function(options) {
    if (!options.hasOwnProperty("startTime") || !options.hasOwnProperty("endTime") ||
        !options.hasOwnProperty("text")) {
      throw new Error("You must at least have start time, end time, and text.");
    }
    var cue = new root.VTTCue(options.startTime, options.endTime, options.text);
    for (var key in options) {
      if (cue.hasOwnProperty(key)) {
        cue[key] = options[key];
      }
    }
    return cue;
  };

  root.VTTCue.fromJSON = function(json) {
    return this.create(JSON.parse(json));
  };

}(this));

},{"./vttcue":395}],395:[function(_dereq_,module,exports){
/**
 * Copyright 2013 vtt.js Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

(function(root, vttjs) {

  var autoKeyword = "auto";
  var directionSetting = {
    "": true,
    "lr": true,
    "rl": true
  };
  var alignSetting = {
    "start": true,
    "middle": true,
    "end": true,
    "left": true,
    "right": true
  };

  function findDirectionSetting(value) {
    if (typeof value !== "string") {
      return false;
    }
    var dir = directionSetting[value.toLowerCase()];
    return dir ? value.toLowerCase() : false;
  }

  function findAlignSetting(value) {
    if (typeof value !== "string") {
      return false;
    }
    var align = alignSetting[value.toLowerCase()];
    return align ? value.toLowerCase() : false;
  }

  function extend(obj) {
    var i = 1;
    for (; i < arguments.length; i++) {
      var cobj = arguments[i];
      for (var p in cobj) {
        obj[p] = cobj[p];
      }
    }

    return obj;
  }

  function VTTCue(startTime, endTime, text) {
    var cue = this;
    var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
    var baseObj = {};

    if (isIE8) {
      cue = document.createElement('custom');
    } else {
      baseObj.enumerable = true;
    }

    /**
     * Shim implementation specific properties. These properties are not in
     * the spec.
     */

    // Lets us know when the VTTCue's data has changed in such a way that we need
    // to recompute its display state. This lets us compute its display state
    // lazily.
    cue.hasBeenReset = false;

    /**
     * VTTCue and TextTrackCue properties
     * http://dev.w3.org/html5/webvtt/#vttcue-interface
     */

    var _id = "";
    var _pauseOnExit = false;
    var _startTime = startTime;
    var _endTime = endTime;
    var _text = text;
    var _region = null;
    var _vertical = "";
    var _snapToLines = true;
    var _line = "auto";
    var _lineAlign = "start";
    var _position = 50;
    var _positionAlign = "middle";
    var _size = 50;
    var _align = "middle";

    Object.defineProperty(cue,
      "id", extend({}, baseObj, {
        get: function() {
          return _id;
        },
        set: function(value) {
          _id = "" + value;
        }
      }));

    Object.defineProperty(cue,
      "pauseOnExit", extend({}, baseObj, {
        get: function() {
          return _pauseOnExit;
        },
        set: function(value) {
          _pauseOnExit = !!value;
        }
      }));

    Object.defineProperty(cue,
      "startTime", extend({}, baseObj, {
        get: function() {
          return _startTime;
        },
        set: function(value) {
          if (typeof value !== "number") {
            throw new TypeError("Start time must be set to a number.");
          }
          _startTime = value;
          this.hasBeenReset = true;
        }
      }));

    Object.defineProperty(cue,
      "endTime", extend({}, baseObj, {
        get: function() {
          return _endTime;
        },
        set: function(value) {
          if (typeof value !== "number") {
            throw new TypeError("End time must be set to a number.");
          }
          _endTime = value;
          this.hasBeenReset = true;
        }
      }));

    Object.defineProperty(cue,
      "text", extend({}, baseObj, {
        get: function() {
          return _text;
        },
        set: function(value) {
          _text = "" + value;
          this.hasBeenReset = true;
        }
      }));

    Object.defineProperty(cue,
      "region", extend({}, baseObj, {
        get: function() {
          return _region;
        },
        set: function(value) {
          _region = value;
          this.hasBeenReset = true;
        }
      }));

    Object.defineProperty(cue,
      "vertical", extend({}, baseObj, {
        get: function() {
          return _vertical;
        },
        set: function(value) {
          var setting = findDirectionSetting(value);
          // Have to check for false because the setting an be an empty string.
          if (setting === false) {
            throw new SyntaxError("An invalid or illegal string was specified.");
          }
          _vertical = setting;
          this.hasBeenReset = true;
        }
      }));

    Object.defineProperty(cue,
      "snapToLines", extend({}, baseObj, {
        get: function() {
          return _snapToLines;
        },
        set: function(value) {
          _snapToLines = !!value;
          this.hasBeenReset = true;
        }
      }));

    Object.defineProperty(cue,
      "line", extend({}, baseObj, {
        get: function() {
          return _line;
        },
        set: function(value) {
          if (typeof value !== "number" && value !== autoKeyword) {
            throw new SyntaxError("An invalid number or illegal string was specified.");
          }
          _line = value;
          this.hasBeenReset = true;
        }
      }));

    Object.defineProperty(cue,
      "lineAlign", extend({}, baseObj, {
        get: function() {
          return _lineAlign;
        },
        set: function(value) {
          var setting = findAlignSetting(value);
          if (!setting) {
            throw new SyntaxError("An invalid or illegal string was specified.");
          }
          _lineAlign = setting;
          this.hasBeenReset = true;
        }
      }));

    Object.defineProperty(cue,
      "position", extend({}, baseObj, {
        get: function() {
          return _position;
        },
        set: function(value) {
          if (value < 0 || value > 100) {
            throw new Error("Position must be between 0 and 100.");
          }
          _position = value;
          this.hasBeenReset = true;
        }
      }));

    Object.defineProperty(cue,
      "positionAlign", extend({}, baseObj, {
        get: function() {
          return _positionAlign;
        },
        set: function(value) {
          var setting = findAlignSetting(value);
          if (!setting) {
            throw new SyntaxError("An invalid or illegal string was specified.");
          }
          _positionAlign = setting;
          this.hasBeenReset = true;
        }
      }));

    Object.defineProperty(cue,
      "size", extend({}, baseObj, {
        get: function() {
          return _size;
        },
        set: function(value) {
          if (value < 0 || value > 100) {
            throw new Error("Size must be between 0 and 100.");
          }
          _size = value;
          this.hasBeenReset = true;
        }
      }));

    Object.defineProperty(cue,
      "align", extend({}, baseObj, {
        get: function() {
          return _align;
        },
        set: function(value) {
          var setting = findAlignSetting(value);
          if (!setting) {
            throw new SyntaxError("An invalid or illegal string was specified.");
          }
          _align = setting;
          this.hasBeenReset = true;
        }
      }));

    /**
     * Other <track> spec defined properties
     */

    // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
    cue.displayState = undefined;

    if (isIE8) {
      return cue;
    }
  }

  /**
   * VTTCue methods
   */

  VTTCue.prototype.getCueAsHTML = function() {
    // Assume WebVTT.convertCueToDOMTree is on the global.
    return WebVTT.convertCueToDOMTree(window, this.text);
  };

  root.VTTCue = root.VTTCue || VTTCue;
  vttjs.VTTCue = VTTCue;
}(this, (this.vttjs || {})));

},{}],396:[function(_dereq_,module,exports){
/**
 * Copyright 2013 vtt.js Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// If we're in Node.js then require VTTRegion so we can extend it, otherwise assume
// VTTRegion is on the global.
if (typeof module !== "undefined" && module.exports) {
  this.VTTRegion = _dereq_("./vttregion").VTTRegion;
}

// Extend VTTRegion with methods to convert to JSON, from JSON, and construct a
// VTTRegion from an options object. The primary purpose of this is for use in the
// vtt.js test suite. It's also useful if you need to work with VTTRegions in
// JSON format.
(function(root) {

  root.VTTRegion.create = function(options) {
    var region = new root.VTTRegion();
    for (var key in options) {
      if (region.hasOwnProperty(key)) {
        region[key] = options[key];
      }
    }
    return region;
  };

  root.VTTRegion.fromJSON = function(json) {
    return this.create(JSON.parse(json));
  };

}(this));

},{"./vttregion":397}],397:[function(_dereq_,module,exports){
/**
 * Copyright 2013 vtt.js Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

(function(root, vttjs) {

  var scrollSetting = {
    "": true,
    "up": true
  };

  function findScrollSetting(value) {
    if (typeof value !== "string") {
      return false;
    }
    var scroll = scrollSetting[value.toLowerCase()];
    return scroll ? value.toLowerCase() : false;
  }

  function isValidPercentValue(value) {
    return typeof value === "number" && (value >= 0 && value <= 100);
  }

  // VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface
  function VTTRegion() {
    var _width = 100;
    var _lines = 3;
    var _regionAnchorX = 0;
    var _regionAnchorY = 100;
    var _viewportAnchorX = 0;
    var _viewportAnchorY = 100;
    var _scroll = "";

    Object.defineProperties(this, {
      "width": {
        enumerable: true,
        get: function() {
          return _width;
        },
        set: function(value) {
          if (!isValidPercentValue(value)) {
            throw new Error("Width must be between 0 and 100.");
          }
          _width = value;
        }
      },
      "lines": {
        enumerable: true,
        get: function() {
          return _lines;
        },
        set: function(value) {
          if (typeof value !== "number") {
            throw new TypeError("Lines must be set to a number.");
          }
          _lines = value;
        }
      },
      "regionAnchorY": {
        enumerable: true,
        get: function() {
          return _regionAnchorY;
        },
        set: function(value) {
          if (!isValidPercentValue(value)) {
            throw new Error("RegionAnchorX must be between 0 and 100.");
          }
          _regionAnchorY = value;
        }
      },
      "regionAnchorX": {
        enumerable: true,
        get: function() {
          return _regionAnchorX;
        },
        set: function(value) {
          if(!isValidPercentValue(value)) {
            throw new Error("RegionAnchorY must be between 0 and 100.");
          }
          _regionAnchorX = value;
        }
      },
      "viewportAnchorY": {
        enumerable: true,
        get: function() {
          return _viewportAnchorY;
        },
        set: function(value) {
          if (!isValidPercentValue(value)) {
            throw new Error("ViewportAnchorY must be between 0 and 100.");
          }
          _viewportAnchorY = value;
        }
      },
      "viewportAnchorX": {
        enumerable: true,
        get: function() {
          return _viewportAnchorX;
        },
        set: function(value) {
          if (!isValidPercentValue(value)) {
            throw new Error("ViewportAnchorX must be between 0 and 100.");
          }
          _viewportAnchorX = value;
        }
      },
      "scroll": {
        enumerable: true,
        get: function() {
          return _scroll;
        },
        set: function(value) {
          var setting = findScrollSetting(value);
          // Have to check for false as an empty string is a legal value.
          if (setting === false) {
            throw new SyntaxError("An invalid or illegal string was specified.");
          }
          _scroll = setting;
        }
      }
    });
  }

  root.VTTRegion = root.VTTRegion || VTTRegion;
  vttjs.VTTRegion = VTTRegion;
}(this, (this.vttjs || {})));

},{}],398:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _videoJs = _dereq_('video.js');

var _videoJs2 = _interopRequireDefault(_videoJs);

// Default options for the plugin.
var defaults = {
  position: 'top-right',
  fadeTime: 3000,
  url: undefined,
  image: undefined
};

/**
 * Sets up the div, img and optional a tags for the plugin.
 *
 * @function setupWatermark
 * @param    {Player} player
 * @param    {Object} [options={}]
 */
var setupWatermark = function setupWatermark(player, options) {
  // Add a div and img tag
  var videoEl = player.el();
  var div = document.createElement('div');
  var img = document.createElement('img');

  div.classList.add('vjs-watermark-content');
  div.classList.add('vjs-watermark-' + options.position);
  img.src = options.image;

  // if a url is provided make the image link to that URL.
  if (options.url) {
    var a = document.createElement('a');

    a.href = options.url;
    // if the user clicks the link pause and open a new window
    a.onclick = function (e) {
      e.preventDefault();
      player.pause();
      window.open(options.url);
    };
    a.appendChild(img);
    div.appendChild(a);
  } else {
    div.appendChild(img);
  }
  videoEl.appendChild(div);
};

/**
 * Fades the watermark image.
 *
 * @function fadeWatermark
 * @param    {Object} [options={
 *                  fadeTime:
 *                  'The number of milliseconds before the inital watermark fade out'}]
 */
var fadeWatermark = function fadeWatermark(options) {
  setTimeout(function () {
    return document.getElementsByClassName('vjs-watermark-content')[0].classList.add('vjs-watermark-fade');
  }, options.fadeTime);
};

/**
 * Function to invoke when the player is ready.
 *
 * This is a great place for your plugin to initialize itself. When this
 * function is called, the player will have its DOM and child components
 * in place.
 *
 * @function onPlayerReady
 * @param    {Player} player
 * @param    {Object} [options={}]
 */
var onPlayerReady = function onPlayerReady(player, options) {
  player.addClass('vjs-watermark');

  // if there is no image set just exit
  if (!options.image) {
    return;
  }
  setupWatermark(player, options);

  // Setup watermark autofade
  if (options.fadeTime === null) {
    return;
  }

  player.on('play', function () {
    return fadeWatermark(options);
  });
};

/**
 * A video.js plugin.
 *
 * In the plugin function, the value of `this` is a video.js `Player`
 * instance. You cannot rely on the player being in a "ready" state here,
 * depending on how the plugin is invoked. This may or may not be important
 * to you; if not, remove the wait for "ready"!
 *
 * @function watermark
 * @param    {Object} [options={}]
 *           An object of options left to the plugin author to define.
 */
var watermark = function watermark(options) {
  var _this = this;

  this.ready(function () {
    onPlayerReady(_this, _videoJs2['default'].mergeOptions(defaults, options));
  });
};

// Register the plugin with video.js.
_videoJs2['default'].plugin('watermark', watermark);

// Include the version number.
watermark.VERSION = '__VERSION__';

exports['default'] = watermark;
module.exports = exports['default'];
},{"video.js":390}],399:[function(_dereq_,module,exports){
var css = ".vjs-dvr .vjs-progress-control{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.vjs-dvr .vjs-live-control{flex:inherit;cursor:pointer}.vjs-dvr .vjs-live-control .vjs-tooltip{line-height:1em}.vjs-dvr-live .vjs-live-control .vjs-tooltip{display:none}.vjs-dvr-live .vjs-live-control{cursor:default}.vjs-dvr:not(.vjs-dvr-live) .vjs-live-display:before{background:#566377}"; (_dereq_("browserify-css").createStyle(css, { "href": "src/css/dvr.css" }, { "insertAt": "bottom" })); module.exports = css;
},{"browserify-css":171}],400:[function(_dereq_,module,exports){
var css = ".video-js .vjs-button.vjs-next-button{order:.1}.video-js .vjs-button.vjs-next-button .vjs-button-icon{width:3.6em;height:100%;cursor:pointer;opacity:.9}.video-js .vjs-button.vjs-next-button .vjs-button-icon>svg{position:absolute;width:90%;height:90%;padding:0;top:0;left:0;right:0;bottom:0;margin:auto;display:block;transition:opacity 200ms ease}.video-js .vjs-button.vjs-next-button .vjs-button-icon:hover{opacity:1}.video-js.vjs-ad-playing .vjs-button.vjs-next-button{display:none}"; (_dereq_("browserify-css").createStyle(css, { "href": "src/css/next.css" }, { "insertAt": "bottom" })); module.exports = css;
},{"browserify-css":171}],401:[function(_dereq_,module,exports){
var css = ".video-js .vjs-button.vjs-share-button{position:absolute;width:3.6em;height:3.6em;top:1.2em;right:1.2em;border-radius:50%;cursor:pointer;z-index:2;background:rgba(28,28,28,.9)}.vjs-ios-skin .vjs-button.vjs-share-button{background:0 0}.vjs-ios-skin.vjs-has-started .vjs-button.vjs-share-button{top:45px}.video-js .vjs-button.vjs-share-button .vjs-button-icon{position:absolute;width:2em;height:2em;top:50%;left:50%;margin:-1em 0 0 -1.1em}.video-js.vjs-ad-playing .vjs-button.vjs-share-button{display:none}.video-js.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-share-button{opacity:0;transition:visibility .25s,opacity .25s}.video-js .vjs-button.vjs-share-button .vjs-tooltip,.video-js .vjs-modal-dialog.vjs-share-dialog .vjs-close-button .vjs-tooltip{top:3.2em}.video-js .vjs-modal-dialog.vjs-share-dialog{background-color:rgba(0,0,0,.7);z-index:5}.video-js.vjs-ima-ad .vjs-button.vjs-share-button,.video-js.vjs-ima-ad .vjs-modal-dialog.vjs-share-dialog{z-index:1112}.video-js .vjs-modal-dialog.vjs-share-dialog .vjs-modal-dialog-content{top:50%;height:7.6em;padding:0;margin-top:-3.8em;text-align:center;font-size:1em}.video-js .vjs-modal-dialog.vjs-share-dialog .vjs-share-dialog-title{font-size:1.6em;margin-bottom:.5em}.video-js .vjs-modal-dialog.vjs-share-dialog .vjs-share-dialog-buttons .vjs-button{width:auto;height:auto;display:inline-block}.video-js .vjs-modal-dialog.vjs-share-dialog .vjs-share-link{display:block;width:4.4em;height:4.4em;padding:.3em;background-repeat:no-repeat;background-size:3.4em;background-position:center center}.video-js .vjs-modal-dialog.vjs-share-dialog .vjs-share-link svg{pointer-events:none}.video-js.vjs-fullscreen:not(.vjs-small) .vjs-button.vjs-share-button,.video-js.vjs-fullscreen:not(.vjs-small) .vjs-modal-dialog.vjs-share-dialog{font-size:150%}"; (_dereq_("browserify-css").createStyle(css, { "href": "src/css/share.css" }, { "insertAt": "bottom" })); module.exports = css;
},{"browserify-css":171}],402:[function(_dereq_,module,exports){
var css = ".vjs-ad-playing.vjs-ad-playing .vjs-progress-control:after{width:100%;height:100%;position:absolute;left:0;bottom:0;content:\" \";user-select:none;-ms-user-select:none;-moz-user-select:none;-webkit-user-select:none}.vjs-ad-playing.vjs-ad-playing .vjs-play-progress{background-color:#ffe400}.vjs-ad-loading .vjs-loading-spinner{display:block}.vjs-ad-playing .vjs-audio-button,.vjs-ad-playing .vjs-captions-button{display:none}.vjs-ad-loading .vjs-loading-spinner:after,.vjs-ad-loading .vjs-loading-spinner:before{-webkit-animation:vjs-spinner-spin 1.1s cubic-bezier(0.6,.2,0,.8) infinite,vjs-spinner-fade 1.1s linear infinite;animation:vjs-spinner-spin 1.1s cubic-bezier(0.6,.2,0,.8) infinite,vjs-spinner-fade 1.1s linear infinite}.vjs-ad-loading .vjs-loading-spinner:before{border-top-color:#fff}.vjs-ad-loading .vjs-loading-spinner:after{border-top-color:#fff;-webkit-animation-delay:.44s;animation-delay:.44s}"; (_dereq_("browserify-css").createStyle(css, { "href": "src/css/videojs-contrib-ads.css" }, { "insertAt": "bottom" })); module.exports = css;
},{"browserify-css":171}],403:[function(_dereq_,module,exports){
var css = ".text-shadow{text-shadow:0 0 2px rgba(0,0,0,.5)}.vjs5-hola-skin{font-family:Roboto,Arial,Helvetica,sans-serif;-webkit-font-smoothing:antialiased}.vjs5-hola-skin .hola-circle-icon{display:block;width:1em;height:1em;box-sizing:border-box;border-radius:50%;border-style:solid;border-width:.3em;border-color:#fff;background:#00b7f1}.vjs5-hola-skin .vjs-control-bar{color:#fff}.vjs5-hola-skin .vjs-control-bar>:not(.vjs-gradient){z-index:2}.vjs5-hola-skin .vjs-big-play-button{top:0;left:0;width:100%;height:100%;font-size:1em;border:0;opacity:1}.vjs5-hola-skin .vjs-big-play-button:before{content:none}.vjs5-hola-skin .vjs-big-play-button .vjs-button-icon{position:absolute;height:4.8em;width:6.8em;left:50%;top:50%;margin-left:-3.4em;margin-top:-2.4em;fill:rgba(33,33,33,.8)}.vjs5-hola-skin .vjs-big-play-button:hover .vjs-button-icon{fill:#00b7f1}.vjs5-hola-skin .vjs-big-play-button,.vjs5-hola-skin .vjs-big-play-button:focus,.vjs5-hola-skin:hover .vjs-big-play-button,.vjs5-hola-skin:hover .vjs-big-play-button:focus{background:0 0}.vjs5-hola-skin.vjs-error-hidden .vjs-big-play-button,.vjs5-hola-skin.vjs-error-hidden .vjs-share-button{display:none!important}.vjs5-hola-skin.vjs-error-hidden *{cursor:default}.vjs5-hola-skin.vjs-error .vjs-big-play-button,.vjs5-hola-skin.vjs-error .vjs-loading-spinner,.vjs5-hola-skin.vjs-error .vjs-share-button,.vjs5-hola-skin.vjs-error .vjs-top-bar{display:none!important}.vjs5-hola-skin.vjs-ima-ad:not(.vjs-has-started) .vjs-big-play-button{z-index:1112}.vjs5-hola-skin.vjs-paused.vjs-pos-ended .vjs-big-play-button,.vjs5-hola-skin.vjs-paused.vjs-pos-ended .vjs-poster{display:block}.vjs5-hola-skin.vjs-ad-loading .vjs-big-play-button,.vjs5-hola-skin.vjs-ad-playing .vjs-big-play-button,.vjs5-hola-skin.vjs-seeking .vjs-big-play-button,.vjs5-hola-skin.vjs-using-native-controls .vjs-big-play-button,.vjs5-hola-skin.vjs-waiting .vjs-big-play-button{display:none!important}.vjs5-hola-skin.vjs-has-started .vjs-poster{opacity:0;transition:opacity 250ms ease}.vjs5-hola-skin .vjs-control{margin:0;padding:0;text-align:left}.vjs5-hola-skin .vjs-control>.vjs-button-icon,.vjs5-hola-skin .vjs-control>svg{position:relative;box-sizing:content-box;display:inline-block;width:3.6em;height:100%;margin:0;padding:0}.vjs5-hola-skin .vjs-control:focus .vjs-menu{text-shadow:none}.vjs5-hola-skin .vjs-control-bar{height:3.6em;background:0 0;overflow:visible;text-shadow:0 0 2px rgba(0,0,0,.5)}.vjs5-hola-skin .vjs-control-bar>.vjs-spacer{display:block;flex-grow:1;order:5}.vjs5-hola-skin .vjs-control-bar>.vjs-control:before{line-height:2}.vjs5-hola-skin .vjs-control-bar>.vjs-control,.vjs5-hola-skin .vjs-control-bar>.vjs-progress-control,.vjs5-hola-skin .vjs-control-bar>.vjs-time-control{margin-top:0}.vjs5-hola-skin .vjs-control-bar>.vjs-captions-button{order:9}.vjs5-hola-skin .vjs-control-bar>.vjs-playback-rate{order:8}.vjs5-hola-skin.vjs-live:not(.vjs-dvr) .vjs-control-bar>.vjs-control,.vjs5-hola-skin.vjs-live:not(.vjs-dvr) .vjs-control-bar>.vjs-time-control{margin-top:0}.vjs5-hola-skin.vjs-live .vjs-control-bar .vjs-time-control.vjs-current-time,.vjs5-hola-skin.vjs-live .vjs-control-bar .vjs-time-control.vjs-duration,.vjs5-hola-skin.vjs-live .vjs-control-bar .vjs-time-control.vjs-time-divider{display:none}.vjs5-hola-skin.vjs-live .vjs-live-display{font-size:1.3em;color:#eee;text-shadow:none}.vjs5-hola-skin.vjs-live .vjs-live-display:before{display:inline-block;width:6px;height:6px;margin-right:.4em;vertical-align:4%;background:#f51717;content:'';border-radius:50%}.vjs5-hola-skin.vjs-live.vjs-fullscreen:not(.vjs-small) .vjs-live-display:before{width:10px;height:10px}.vjs5-hola-skin .vjs-control-bar .vjs-progress-control .vjs-mouse-display-tooltip,.vjs5-hola-skin .vjs-control-bar .vjs-progress-control .vjs-volume-display-tooltip,.vjs5-hola-skin .vjs-tooltip{top:-1.84615385em;padding:.38461538em .69230769em;border-radius:.15384615em;font-size:1.3em;font-family:Roboto,Arial,Helvetica,sans-serif;line-height:1.15384615em;font-weight:500;text-align:center;text-shadow:none;clip:auto;pointer-events:none;color:#eee;background-color:#1c1c1c;background-color:rgba(28,28,28,.9)}.vjs5-hola-skin .vjs-tooltip{top:-3em;left:0;width:auto;height:auto;margin:0}.vjs5-hola-skin .vjs-menu{text-shadow:0 0 2px rgba(0,0,0,.5)}.vjs5-hola-skin .vjs-menu-item{text-align:left;padding-left:.5em;text-transform:none}.vjs5-hola-skin .vjs-menu-item[role=menuitemcheckbox]{padding:.3em .4em .2em 2em}.vjs5-hola-skin .vjs-menu-label{pointer-events:none}.vjs5-hola-skin .vjs-menu-indent{padding-left:1.5em}.vjs5-hola-skin .vjs-menu-item-link>.vjs-menu-link{display:block;color:inherit;text-decoration:inherit;text-transform:inherit;white-space:nowrap;border:none}.vjs5-hola-skin .vjs-menu-button[aria-expanded=true]>.vjs-tooltip-active{display:none}.vjs5-hola-skin .vjs-play-control{cursor:pointer;margin-left:1em;width:4.4em;order:0}.vjs5-hola-skin .vjs-play-control.vjs-paused:before,.vjs5-hola-skin .vjs-play-control.vjs-playing:before,.vjs5-hola-skin .vjs-play-control:before{content:''}.vjs5-hola-skin .vjs-play-control.vjs-playing .vjs-tooltip{margin-left:.5em}.vjs5-hola-skin .vjs-play-control.vjs-play-control-ad>.vjs-button-icon,.vjs5-hola-skin .vjs-play-control:not(.vjs-play-control-replay)>.vjs-button-icon{box-sizing:content-box;position:absolute;width:1.2em;height:1.6em;padding:0;top:0;left:0;right:0;bottom:0;margin:auto}.vjs5-hola-skin .vjs-play-control.vjs-play-control-ad>.vjs-button-icon:after,.vjs5-hola-skin .vjs-play-control.vjs-play-control-ad>.vjs-button-icon:before,.vjs5-hola-skin .vjs-play-control:not(.vjs-play-control-replay)>.vjs-button-icon:after,.vjs5-hola-skin .vjs-play-control:not(.vjs-play-control-replay)>.vjs-button-icon:before{content:'';position:absolute;top:50%;width:0;transform:translateY(-50%);border-top:.4em solid transparent;border-bottom:.4em solid transparent;border-left:.6em solid #fff;transition:border .2s linear,height .2s linear}.vjs5-hola-skin .vjs-play-control.vjs-play-control-ad>.vjs-button-icon:before,.vjs5-hola-skin .vjs-play-control:not(.vjs-play-control-replay)>.vjs-button-icon:before{left:0;height:.8em}.vjs5-hola-skin .vjs-play-control.vjs-play-control-ad>.vjs-button-icon:after,.vjs5-hola-skin .vjs-play-control:not(.vjs-play-control-replay)>.vjs-button-icon:after{right:0;height:0}.vjs5-hola-skin .vjs-play-control.vjs-play-control-ad.vjs-playing>.vjs-button-icon:after,.vjs5-hola-skin .vjs-play-control.vjs-play-control-ad.vjs-playing>.vjs-button-icon:before,.vjs5-hola-skin .vjs-play-control:not(.vjs-play-control-replay).vjs-playing>.vjs-button-icon:after,.vjs5-hola-skin .vjs-play-control:not(.vjs-play-control-replay).vjs-playing>.vjs-button-icon:before{border-top-width:0;border-bottom-width:0;border-left-width:.4em;height:100%}.vjs5-hola-skin .vjs-play-control.vjs-play-control-replay:not(.vjs-play-control-ad)>.vjs-button-icon{background-image:url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjM2IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIzNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxwYXRoIGQ9Ik0xMiA1VjFMNyA2bDUgNVY3YzMuMzEgMCA2IDIuNjkgNiA2cy0yLjY5IDYtNiA2LTYtMi42OS02LTZINGMwIDQuNDIgMy41OCA4IDggOHM4LTMuNTggOC04LTMuNTgtOC04LTh6Ii8+Cjwvc3ZnPg==);background-repeat:no-repeat;background-size:2.4em;background-position:center center}.vjs5-hola-skin .vjs-play-control>.vjs-button-icon{opacity:.9}.vjs5-hola-skin .vjs-play-control:hover>.vjs-button-icon{opacity:1}.vjs5-hola-skin .ima-play-pause-div>svg{fill:#fff;stroke:#fff;stroke-width:0;opacity:.9}.vjs5-hola-skin .ima-play-pause-div:hover>svg{opacity:1}.vjs5-hola-skin.vjs-using-native-controls .vjs-big-play-button,.vjs5-hola-skin.vjs-using-native-controls .vjs-big-play-button.vjs-hidden{display:none!important}.vjs5-hola-skin .vjs-progress-control{position:absolute;left:0;right:0;z-index:10;width:auto;bottom:100%;height:1.5em;margin:0;padding:0;overflow:visible;-webkit-align-items:flex-end;-ms-flex-align:flex-end;align-items:flex-end}.vjs5-hola-skin .vjs-progress-control .vjs-mouse-display,.vjs5-hola-skin .vjs-progress-control .vjs-play-progress{font-family:Roboto,Arial,Helvetica,sans-serif;overflow:visible}.vjs5-hola-skin .vjs-progress-control .vjs-mouse-display:before,.vjs5-hola-skin .vjs-progress-control .vjs-play-progress:before{content:none;display:block;width:1em;height:1em;box-sizing:border-box;border-radius:50%;border-style:solid;border-width:.3em;border-color:#fff;background:#00b7f1;font-size:1.6em}.vjs5-hola-skin .vjs-progress-control .vjs-mouse-display:after,.vjs5-hola-skin .vjs-progress-control .vjs-play-progress:after{display:none;padding:.2em .5em .5em}.vjs5-hola-skin .vjs-progress-control .vjs-mouse-display.vjs-keep-tooltips-inside:after,.vjs5-hola-skin .vjs-progress-control .vjs-play-progress.vjs-keep-tooltips-inside:after{display:none!important;content:none!important}.vjs5-hola-skin .vjs-progress-control .vjs-mouse-display-tooltip{display:none}.vjs5-hola-skin .vjs-progress-control .vjs-play-progress{background:#00b7f1}.vjs5-hola-skin .vjs-progress-control .vjs-progress-holder{height:3px;margin:0 1em;overflow:visible;transition:height .1s ease,margin .1s ease}.vjs5-hola-skin .vjs-progress-control .vjs-progress-holder .vjs-load-progress,.vjs5-hola-skin .vjs-progress-control .vjs-progress-holder .vjs-load-progress div,.vjs5-hola-skin .vjs-progress-control .vjs-progress-holder .vjs-play-progress,.vjs5-hola-skin .vjs-progress-control .vjs-progress-holder .vjs-tooltip-progress-bar{height:100%}.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control.vjs-touch .vjs-mouse-display:before,.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control.vjs-touch .vjs-play-progress:before,.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control:hover .vjs-mouse-display:before,.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control:hover .vjs-play-progress:before{content:''}.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control.vjs-touch .vjs-mouse-display,.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control:hover .vjs-mouse-display{background:0 0}.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control.vjs-touch .vjs-mouse-display:before,.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control:hover .vjs-mouse-display:before{position:absolute;top:-.33333333em;right:-.5em;background:#bbb;opacity:.7}.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control.vjs-touch .vjs-play-progress:after,.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control.vjs-touch .vjs-tooltip-progress-bar,.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control:hover .vjs-play-progress:after,.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control:hover .vjs-tooltip-progress-bar{display:none!important}.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control.vjs-touch .vjs-mouse-display,.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control:hover .vjs-mouse-display{display:transparent}.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control.vjs-touch .vjs-mouse-display-tooltip,.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control:hover .vjs-mouse-display-tooltip{display:block}.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control.vjs-touch .vjs-progress-holder,.vjs5-hola-skin:not(.vjs-ad-playing) .vjs-progress-control:hover .vjs-progress-holder{font-size:1em;height:5px}.vjs5-hola-skin:not(.vjs-ad-playing).vjs-mobile .vjs-progress-control .vjs-play-progress:before{content:'';top:auto;bottom:-.33333333em}.vjs5-hola-skin .vjs-live-control{line-height:3.6em;order:4}.vjs5-hola-skin .vjs-slider .vjs-slider-gap-left,.vjs5-hola-skin .vjs-slider .vjs-slider-gap-right{position:absolute;top:0;width:1em;height:100%}.vjs5-hola-skin .vjs-slider .vjs-slider-gap-left{left:-1em}.vjs5-hola-skin .vjs-slider .vjs-slider-gap-right{right:-1em}.vjs5-hola-skin .vjs-ads-label{order:0;line-height:3em}.vjs5-hola-skin .vjs-volume-bar{margin:1.35em .6em}.vjs5-hola-skin .vjs-volume-bar.vjs-slider-horizontal{height:.5em;margin-top:1.1em;margin-bottom:1.1em;padding:.7em 0;background:0 0}.vjs5-hola-skin .vjs-volume-bar.vjs-slider-horizontal:after,.vjs5-hola-skin .vjs-volume-bar.vjs-slider-horizontal:before{height:.4em;top:50%;margin-top:-.15em}.vjs5-hola-skin .vjs-volume-bar.vjs-slider-horizontal:before{content:'';background:#73859f;background:rgba(115,133,159,.5)}.vjs5-hola-skin .vjs-volume-bar.vjs-slider-horizontal .vjs-volume-level,.vjs5-hola-skin .vjs-volume-bar.vjs-slider-horizontal:before{position:absolute;left:0;top:50%;margin-top:-.15em;height:.3em;width:100%}.vjs5-hola-skin .vjs-volume-bar.vjs-slider-horizontal .vjs-volume-level{overflow:visible;background-color:#fff}.vjs5-hola-skin .vjs-volume-bar.vjs-slider-horizontal .vjs-slider-gap-left{width:15px;left:-15px}.vjs5-hola-skin .vjs-volume-bar.vjs-slider-horizontal .vjs-slider-gap-right{width:20px;right:-20px}.vjs5-hola-skin .vjs-volume-bar .vjs-volume-display:before,.vjs5-hola-skin .vjs-volume-bar.vjs-slider-horizontal .vjs-volume-level:before{display:block;width:1em;height:1em;box-sizing:border-box;border-radius:50%;border-style:solid;border-width:.3em;border-color:#fff;background:#fff;top:-.4em;font-size:1.2em;z-index:2}.vjs5-hola-skin .vjs-volume-bar.vjs-slider-horizontal .vjs-volume-level:before{content:''}.vjs5-hola-skin .vjs-volume-bar .vjs-volume-display{top:0;background:0 0;overflow:visible}.vjs5-hola-skin .vjs-volume-bar .vjs-volume-display:before{content:'';position:absolute;left:-.5em;top:50%;margin-top:-.5em;height:1em;z-index:1;background:#bbb;opacity:.7}.vjs5-hola-skin .vjs-volume-menu-button{margin-right:1em;cursor:default;overflow:visible;width:3.6em}.vjs5-hola-skin .vjs-volume-menu-button .vjs-menu-content{width:0;transition:width 200ms ease}.vjs5-hola-skin .vjs-volume-menu-button>.vjs-button-icon{width:3.6em;cursor:pointer;opacity:.9}.vjs5-hola-skin .vjs-volume-menu-button>.vjs-button-icon>svg{position:absolute;left:.2em;top:0;display:block;width:3.6em;padding:0;height:100%;fill:#fff;transition:opacity 200ms ease}.vjs5-hola-skin .vjs-volume-menu-button>.vjs-button-icon:hover{opacity:1}.vjs5-hola-skin .vjs-volume-menu-button.vjs-volume-menu-button-horizontal{order:1}.vjs5-hola-skin .vjs-volume-menu-button.vjs-volume-menu-button-horizontal:before{content:none;display:none}.vjs5-hola-skin .vjs-volume-menu-button.vjs-volume-menu-button-horizontal>.vjs-button-icon{height:100%;background-size:55%;background-repeat:no-repeat;background-position:center;transition:opacity 200ms ease}.vjs5-hola-skin .vjs-volume-menu-button.vjs-volume-menu-button-horizontal>.vjs-button-icon path{transition:opacity 250ms ease}.vjs5-hola-skin .vjs-volume-menu-button.vjs-volume-menu-button-horizontal.vjs-vol-1>.vjs-button-icon .volume-level-2{opacity:0}.vjs5-hola-skin .vjs-volume-menu-button.vjs-volume-menu-button-horizontal.vjs-vol-2>.vjs-button-icon .volume-level-2{opacity:1}.vjs5-hola-skin .vjs-menu-button-inline{transition:all 200ms}.vjs5-hola-skin .vjs-menu-button-inline.vjs-show-volume-button .vjs-menu,.vjs5-hola-skin .vjs-menu-button-inline.vjs-slider-active .vjs-menu,.vjs5-hola-skin .vjs-menu-button-inline:hover .vjs-menu{display:block;opacity:1}.vjs5-hola-skin .vjs-volume-menu-button:focus:not(.vjs-show-volume-button){width:3.6em}.vjs5-hola-skin .vjs-volume-menu-button:focus:not(.vjs-show-volume-button) .vjs-menu{display:none}.vjs5-hola-skin .vjs-volume-menu-button.vjs-volume-menu-button-horizontal .vjs-lock-showing .vjs-menu-content,.vjs5-hola-skin .vjs-volume-menu-button.vjs-volume-menu-button-horizontal.vjs-show-volume-button .vjs-menu-content,.vjs5-hola-skin .vjs-volume-menu-button.vjs-volume-menu-button-horizontal.vjs-slider-active .vjs-menu-content,.vjs5-hola-skin .vjs-volume-menu-button.vjs-volume-menu-button-horizontal:hover .vjs-menu-content{height:3.6em;width:6.2em}.vjs5-hola-skin .vjs-menu-button-inline.vjs-show-volume-button:not(.vjs-no-volume),.vjs5-hola-skin .vjs-menu-button-inline.vjs-slider-active:not(.vjs-no-volume),.vjs5-hola-skin .vjs-menu-button-inline:hover:not(.vjs-no-volume),.vjs5-hola-skin.vjs-no-flex .vjs-menu-button-inline.vjs-slider-active:not(.vjs-no-volume),.vjs5-hola-skin.vjs-no-flex .vjs-menu-button-inline:hover:not(.vjs-no-volume){width:10em}.vjs5-hola-skin .vjs-fullscreen-control{width:auto;margin-right:1em;order:20}.vjs5-hola-skin .vjs-fullscreen-control:before{content:'';display:none}.vjs5-hola-skin .vjs-fullscreen-control .vjs-button-icon{position:relative;height:100%;padding:0;opacity:.9;fill:#fff}.vjs5-hola-skin .vjs-fullscreen-control:hover .vjs-button-icon{opacity:1}.vjs5-hola-skin .vjs-fullscreen-control:hover .vjs-button-icon>svg>g:nth-child(1){animation:vjs-fullscreen-1 .3s ease}.vjs5-hola-skin .vjs-fullscreen-control:hover .vjs-button-icon>svg>g:nth-child(2){animation:vjs-fullscreen-2 .3s ease}.vjs5-hola-skin .vjs-fullscreen-control:hover .vjs-button-icon>svg>g:nth-child(3){animation:vjs-fullscreen-3 .3s ease}.vjs5-hola-skin .vjs-fullscreen-control:hover .vjs-button-icon>svg>g:nth-child(4){animation:vjs-fullscreen-4 .3s ease}.vjs5-hola-skin .vjs-fullscreen-control .vjs-tooltip{margin-left:-.8em}@keyframes vjs-fullscreen-1{50%{transform:translate(-1px,-1px)}}@keyframes vjs-fullscreen-2{50%{transform:translate(1px,-1px)}}@keyframes vjs-fullscreen-3{50%{transform:translate(1px,1px)}}@keyframes vjs-fullscreen-4{50%{transform:translate(-1px,1px)}}.vjs5-hola-skin.vjs-fullscreen .vjs-fullscreen-control .vjs-fullscreen-icon,.vjs5-hola-skin:not(.vjs-fullscreen) .vjs-fullscreen-control .vjs-exit-fullscreen-icon{display:none}.vjs5-hola-skin.vjs-fullscreen .vjs-fullscreen-control:hover .vjs-button-icon{opacity:1}.vjs5-hola-skin .vjs-time-control.vjs-current-time,.vjs5-hola-skin .vjs-time-control.vjs-duration,.vjs5-hola-skin .vjs-time-control.vjs-time-divider{display:inline-block;width:auto;min-width:0;padding:0;font-size:1.3em;line-height:2.76923077em;color:#eee}.vjs5-hola-skin .vjs-time-control.vjs-current-time{order:2}.vjs5-hola-skin .vjs-time-control.vjs-time-divider{order:3}.vjs5-hola-skin .vjs-time-control.vjs-time-divider span:after,.vjs5-hola-skin .vjs-time-control.vjs-time-divider span:before{content:'\\00a0'}.vjs5-hola-skin .vjs-time-control.vjs-duration{padding-right:1em;order:4}.vjs5-hola-skin .vjs-time-control.vjs-remaining-time{display:none}.vjs5-hola-skin.vjs-show-controls-before-start .vjs-control-bar{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;visibility:visible;opacity:1}.vjs5-hola-skin.vjs-show-time-for-live .vjs-control-bar .vjs-time-control.vjs-current-time,.vjs5-hola-skin.vjs-show-time-for-live .vjs-control-bar .vjs-time-control.vjs-duration,.vjs5-hola-skin.vjs-show-time-for-live .vjs-control-bar .vjs-time-control.vjs-time-divider{display:inline-block!important}.vjs5-hola-skin.vjs-small .vjs-time-control.vjs-duration{padding-right:0}.vjs5-hola-skin.vjs-small .vjs-volume-menu-button{margin-right:0}.vjs5-hola-skin .vjs-slider-padding{position:absolute;width:100%;height:2em;top:-1.3em}.vjs5-hola-skin .vjs-play-animation{display:none;border-radius:50%;border:0 solid transparent;font-size:2em;position:absolute;height:3em;width:3em;line-height:3em;left:50%;top:50%;margin-left:-1.5em;margin-top:-1.5em;background-color:#1c1c1c;background-color:rgba(28,28,28,.6);animation:vjs-play-animation .5s linear 1 normal forwards}.vjs5-hola-skin .vjs-play-animation:before{position:absolute;top:0;left:0;width:100%;height:100%;font-size:2em;content:'';background-repeat:no-repeat;background-size:1.2em;background-position:center center}.vjs5-hola-skin.vjs-paused .vjs-play-animation:before{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xMyAtMTAgNDAgNDAiIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIgZmlsbD0iI2ZmZiI+CiAgICA8Zz48cGF0aCBkPSJNIDAsMCA1LDAgNSwyMCAwLDIwIE0gOSwwIDE0LDAgMTQsMjAgOSwyMCBaIi8+PC9nPgo8L3N2Zz4=)}.vjs5-hola-skin.vjs-playing .vjs-play-animation:before{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9Ii0xNSAtMTAgNDAgNDAiIGhlaWdodD0iMjQiIHdpZHRoPSIyNCIgZmlsbD0iI2ZmZiI+CiAgICA8Zz48cGF0aCBkPSJNIDAsMCAwLDIwIDE0LDEwIFoiLz48L2c+Cjwvc3ZnPg==)}@keyframes vjs-play-animation{0%{opacity:1}to{opacity:0;transform:scale(2)}}.vjs5-hola-skin .vjs-loading-spinner{opacity:1;border:none;width:6.4em;height:6.4em;margin:-3.2em 0 0 -3.2em;animation:vjs-loading-spinner 1.5682353s linear infinite}.vjs5-hola-skin .vjs-loading-spinner:after,.vjs5-hola-skin .vjs-loading-spinner:before{content:none}.vjs5-hola-skin .vjs-loading-spinner .vjs-spinner-rotator{position:absolute;width:100%;height:100%;animation:vjs-spinner-rotator 5332ms cubic-bezier(0.4,0,.2,1) infinite both}.vjs5-hola-skin .vjs-loading-spinner .vjs-spinner-left,.vjs5-hola-skin .vjs-loading-spinner .vjs-spinner-right{position:absolute;top:0;bottom:0;overflow:hidden}.vjs5-hola-skin .vjs-loading-spinner .vjs-spinner-left:before,.vjs5-hola-skin .vjs-loading-spinner .vjs-spinner-right:before{content:'';box-sizing:border-box;position:absolute;width:200%;height:100%;border-style:solid;border-color:#ddd #ddd transparent;border-radius:50%;border-width:.6em}.vjs5-hola-skin .vjs-loading-spinner .vjs-spinner-left{right:49%;left:0}.vjs5-hola-skin .vjs-loading-spinner .vjs-spinner-left:before{border-right-color:transparent;animation:vjs-spinner-left 1.333s cubic-bezier(0.4,0,.2,1) infinite both}.vjs5-hola-skin .vjs-loading-spinner .vjs-spinner-right{left:49%;right:0}.vjs5-hola-skin .vjs-loading-spinner .vjs-spinner-right:before{left:-100%;border-left-color:transparent;animation:vjs-spinner-right 1.333s cubic-bezier(0.4,0,.2,1) infinite both}@keyframes vjs-loading-spinner{to{transform:rotate(360deg)}}@keyframes vjs-spinner-rotator{12.5%{transform:rotate(135deg)}25%{transform:rotate(270deg)}37.5%{transform:rotate(405deg)}50%{transform:rotate(540deg)}62.5%{transform:rotate(675deg)}75%{transform:rotate(810deg)}87.5%{transform:rotate(945deg)}to{transform:rotate(1080deg)}}@keyframes vjs-spinner-left{0%{transform:rotate(130deg)}50%{transform:rotate(-5deg)}to{transform:rotate(130deg)}}@keyframes vjs-spinner-right{0%{transform:rotate(-130deg)}50%{transform:rotate(5deg)}to{transform:rotate(-130deg)}}.vjs5-hola-skin .ima-ad-container .vjs-volume-menu-button.vjs-volume-menu-button-horizontal{height:3em}.vjs5-hola-skin .ima-ad-container .vjs-volume-menu-button.vjs-volume-menu-button-horizontal .vjs-volume-bar.vjs-slider-horizontal{margin-top:.6em;margin-bottom:.6em}.vjs5-hola-skin .vjs-modal-dialog .vjs-close-button{top:1.2em;right:1.2em;width:3.6em;height:3.6em;opacity:.8;background-image:url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjZmZmIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICAgIDxwYXRoIGQ9Ik0xOSA2LjQxTDE3LjU5IDUgMTIgMTAuNTkgNi40MSA1IDUgNi40MSAxMC41OSAxMiA1IDE3LjU5IDYuNDEgMTkgMTIgMTMuNDEgMTcuNTkgMTkgMTkgMTcuNTkgMTMuNDEgMTJ6Ii8+Cjwvc3ZnPg==);background-repeat:no-repeat;background-size:2.4em;background-position:center center}.vjs5-hola-skin .vjs-modal-dialog .vjs-close-button:hover{opacity:1}.vjs5-hola-skin .vjs-modal-dialog .vjs-close-button:before{content:none}.vjs5-hola-skin .vjs-caption-settings select{color:#000}.vjs5-hola-skin.vjs-fullscreen{overflow:hidden;z-index:1000;left:0;top:0;bottom:0;right:0}.vjs5-hola-skin .vjs-menu-button-popup .vjs-menu{display:block;width:13em;left:-6em;z-index:2;visibility:hidden}.vjs5-hola-skin .vjs-menu-button-popup .vjs-menu:not(.vjs-menu-popup-on-click){transition:visibility 500ms step-end}.vjs5-hola-skin .vjs-menu-button-popup .vjs-menu ul{bottom:4em}.vjs5-hola-skin .vjs-menu-button-popup .vjs-menu.vjs-menu-popup-on-click.vjs-lock-showing,.vjs5-hola-skin .vjs-menu-button-popup:hover .vjs-menu:not(.vjs-menu-popup-on-click){visibility:visible;transition:visibility 1ms}.vjs5-hola-skin .vjs-menu-button-inline .vjs-menu{left:3.6em}.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small) .vjs-control-bar,.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small) .vjs-loading-spinner,.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small) .vjs-top-bar{font-size:150%}.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small) .vjs-loading-spinner{font-size:200%}.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small).vjs-paused .bumpable-ima-ad-container,.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small).vjs-playing .bumpable-ima-ad-container,.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small).vjs-playing:hover .bumpable-ima-ad-container,.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small).vjs-user-active.vjs-playing .bumpable-ima-ad-container{margin-top:-75px}.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small).vjs-user-inactive.vjs-playing .bumpable-ima-ad-container{margin-top:0}.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small) .vjs-progress-control .vjs-progress-holder{height:5px}.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small):not(.vjs-ad-playing) .vjs-progress-control:hover:hover .vjs-progress-holder{height:8px}.vjs5-hola-skin .vjs-controls-watermark,.vjs5-hola-skin .vjs-controls-watermark:focus,.vjs5-hola-skin .vjs-controls-watermark:hover{width:auto;padding:0 1em;background-color:transparent;background-position:center center;background-repeat:no-repeat;cursor:default;order:6;display:flex;align-items:center}.vjs5-hola-skin .vjs-controls-watermark img,.vjs5-hola-skin .vjs-controls-watermark:focus img,.vjs5-hola-skin .vjs-controls-watermark:hover img{display:inline-block;width:auto;max-width:10em;max-height:3.2em;margin:0;padding:0}.vjs5-hola-skin .vjs-text-track-display{bottom:3.6em;z-index:1}.vjs5-hola-skin .vjs-gradient{position:absolute;pointer-events:none;height:10em;left:0;right:0}.vjs5-hola-skin .vjs-gradient.vjs-top-gradient{top:0;background:linear-gradient(to bottom,rgba(0,0,0,.666) 0,rgba(0,0,0,.492) 19%,rgba(0,0,0,.36) 34%,rgba(0,0,0,.254) 47%,rgba(0,0,0,.185) 56.5%,rgba(0,0,0,.129) 65%,rgba(0,0,0,.084) 73%,rgba(0,0,0,.05) 80.2%,rgba(0,0,0,.028) 86.1%,rgba(0,0,0,.014) 91%,rgba(0,0,0,.005) 95.2%,rgba(0,0,0,.001) 98.2%,rgba(0,0,0,0) 100%)}.vjs5-hola-skin .vjs-gradient.vjs-bottom-gradient{bottom:0;background:linear-gradient(to top,rgba(0,0,0,.666) 0,rgba(0,0,0,.492) 19%,rgba(0,0,0,.36) 34%,rgba(0,0,0,.254) 47%,rgba(0,0,0,.185) 56.5%,rgba(0,0,0,.129) 65%,rgba(0,0,0,.084) 73%,rgba(0,0,0,.05) 80.2%,rgba(0,0,0,.028) 86.1%,rgba(0,0,0,.014) 91%,rgba(0,0,0,.005) 95.2%,rgba(0,0,0,.001) 98.2%,rgba(0,0,0,0) 100%)}.vjs5-hola-skin .vjs-top-bar{position:absolute;pointer-events:none;top:0;left:0;width:100%;opacity:1;transition:visibility .1s,opacity .1s}.vjs5-hola-skin .vjs-top-bar .vjs-video-title{position:absolute;left:12px;padding-left:.22222222em;padding-top:.66666667em;color:#eee;font-size:1.8em;text-shadow:0 0 2px rgba(0,0,0,.5)}.vjs5-hola-skin.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar,.vjs5-hola-skin.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-top-bar{opacity:0;transition:visibility .25s,opacity .25s}.vjs5-hola-skin.video-js{direction:ltr}.vjs5-hola-skin.video-js ul li{list-style-type:none}.vjs-ad-playing .vjs-loading-spinner{display:none!important}.vjs-ad-loading .vjs-loading-spinner{display:block!important}.vjs-ios-skin{color:rgba(255,255,255,.75)}.vjs-ios-skin .vjs-control-bar{box-sizing:border-box;width:auto;background:0 0}.vjs-ios-skin .vjs-control{display:flex;height:100%;align-items:center}.vjs-ios-skin .vjs-control:before{line-height:31px;font-size:18px}.vjs-ios-skin .vjs-tooltip{display:none}.vjs-ios-skin .vjs-audio-button,.vjs-ios-skin .vjs-captions-button,.vjs-ios-skin .vjs-chapters-button,.vjs-ios-skin .vjs-descriptions-button,.vjs-ios-skin .vjs-duration,.vjs-ios-skin .vjs-subtitles-button,.vjs-ios-skin .vjs-time-divider{display:none!important}.vjs-ios-skin .vjs-control-bar,.vjs-ios-skin .vjs-control.vjs-ios-control-box{position:absolute;height:31px;margin:6px;border-radius:8px}.vjs-ios-skin .vjs-big-play-button:after,.vjs-ios-skin .vjs-big-play-button:before,.vjs-ios-skin .vjs-big-play-button>*,.vjs-ios-skin .vjs-control-bar:after,.vjs-ios-skin .vjs-control-bar:before,.vjs-ios-skin .vjs-control-bar>*,.vjs-ios-skin .vjs-control.vjs-ios-control-box:after,.vjs-ios-skin .vjs-control.vjs-ios-control-box:before,.vjs-ios-skin .vjs-control.vjs-ios-control-box>*{z-index:2}.vjs-ios-skin .vjs-control-bar{padding:0 8px}.vjs-ios-skin .vjs-custom-control-spacer{display:flex;margin-right:auto}.vjs-ios-skin .vjs-ios-control-box{display:none}.vjs-ios-skin.vjs-has-started .vjs-ios-control-box{display:block;visibility:visible;opacity:1;transition:visibility .1s,opacity .1s}.vjs-ios-skin.vjs-has-started.vjs-user-inactive.vjs-ad-playing:not(.vjs-ad-paused) .vjs-ios-control-box,.vjs-ios-skin.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-ios-control-box{visibility:hidden;opacity:0;transition:visibility 1s,opacity 1s}.vjs-ios-skin.vjs-has-started .vjs-volume-menu-button{display:block!important}.vjs-ios-skin.vjs-controls-disabled .vjs-ios-control-box .vjs-ios-control-box,.vjs-ios-skin.vjs-controls-disabled .vjs-ios-control-box .vjs-loading-spinner,.vjs-ios-skin.vjs-controls-disabled .vjs-ios-control-box .vjs-share-button,.vjs-ios-skin.vjs-error .vjs-ios-control-box,.vjs-ios-skin.vjs-error .vjs-loading-spinner,.vjs-ios-skin.vjs-error .vjs-share-button,.vjs-ios-skin.vjs-using-native-controls .vjs-ios-control-box .vjs-ios-control-box,.vjs-ios-skin.vjs-using-native-controls .vjs-ios-control-box .vjs-loading-spinner,.vjs-ios-skin.vjs-using-native-controls .vjs-ios-control-box .vjs-share-button{display:none!important}.vjs-ios-skin.video-js.vjs-ad-playing .vjs-ios-control-box,.vjs-ios-skin.vjs-ima-non-linear .vjs-ios-control-box{z-index:1112}.vjs-ios-skin .vjs-ios-background-tint{pointer-events:none;z-index:0!important}.vjs-ios-skin .vjs-ios-background-tint,.vjs-ios-skin .vjs-ios-background-tint>div{position:absolute;top:0;left:0;width:100%;height:100%;border-radius:8px}.vjs-ios-skin .vjs-ios-background-tint>.vjs-ios-blur{background-color:rgba(0,0,0,.85)}.vjs-ios-skin .vjs-ios-background-tint>.vjs-ios-tint{background-color:rgba(255,255,255,.14)}.vjs-ios-skin .vjs-info-overlay,.vjs-ios-skin .vjs-menu>.vjs-menu-content,.vjs-ios-skin .vjs-rightclick-popup,.vjs-ios-skin .vjs-settings-menu,.vjs-ios-skin.video-js .vjs-button.vjs-share-button{background:0 0;color:rgba(255,255,255,.75);box-shadow:none}.vjs-ios-skin .vjs-info-overlay,.vjs-ios-skin .vjs-info-overlay:after,.vjs-ios-skin .vjs-info-overlay:before,.vjs-ios-skin .vjs-menu:not(.vjs-settings-menu)>.vjs-menu-content,.vjs-ios-skin .vjs-menu:not(.vjs-settings-menu)>.vjs-menu-content:after,.vjs-ios-skin .vjs-menu:not(.vjs-settings-menu)>.vjs-menu-content:before,.vjs-ios-skin .vjs-rightclick-popup,.vjs-ios-skin .vjs-rightclick-popup:after,.vjs-ios-skin .vjs-rightclick-popup:before,.vjs-ios-skin .vjs-settings-menu,.vjs-ios-skin .vjs-settings-menu:after,.vjs-ios-skin .vjs-settings-menu:before,.vjs-ios-skin.video-js .vjs-button.vjs-share-button,.vjs-ios-skin.video-js .vjs-button.vjs-share-button:after,.vjs-ios-skin.video-js .vjs-button.vjs-share-button:before{border-radius:8px}.vjs-ios-skin .vjs-info-overlay:after,.vjs-ios-skin .vjs-info-overlay:before,.vjs-ios-skin .vjs-menu:not(.vjs-settings-menu)>.vjs-menu-content:after,.vjs-ios-skin .vjs-menu:not(.vjs-settings-menu)>.vjs-menu-content:before,.vjs-ios-skin .vjs-rightclick-popup:after,.vjs-ios-skin .vjs-rightclick-popup:before,.vjs-ios-skin .vjs-settings-menu:after,.vjs-ios-skin .vjs-settings-menu:before,.vjs-ios-skin.video-js .vjs-button.vjs-share-button:after,.vjs-ios-skin.video-js .vjs-button.vjs-share-button:before{content:' ';display:block;width:100%;height:100%;position:absolute;top:0;left:0;z-index:0!important}.vjs-ios-skin .vjs-info-overlay:before,.vjs-ios-skin .vjs-menu:not(.vjs-settings-menu)>.vjs-menu-content:before,.vjs-ios-skin .vjs-rightclick-popup:before,.vjs-ios-skin .vjs-settings-menu:before,.vjs-ios-skin.video-js .vjs-button.vjs-share-button:before{background-color:rgba(0,0,0,.85)}.vjs-ios-skin .vjs-info-overlay:after,.vjs-ios-skin .vjs-menu:not(.vjs-settings-menu)>.vjs-menu-content:after,.vjs-ios-skin .vjs-rightclick-popup:after,.vjs-ios-skin .vjs-settings-menu:after,.vjs-ios-skin.video-js .vjs-button.vjs-share-button:after{background-color:rgba(255,255,255,.14)}.vjs-ios-skin .vjs-info-overlay>*,.vjs-ios-skin .vjs-menu:not(.vjs-settings-menu)>.vjs-menu-content>*,.vjs-ios-skin .vjs-rightclick-popup>*,.vjs-ios-skin .vjs-settings-menu>*,.vjs-ios-skin.video-js .vjs-button.vjs-share-button>*{position:relative;z-index:2}.vjs-ios-skin.video-js .vjs-button.vjs-share-button,.vjs-ios-skin.video-js .vjs-button.vjs-share-button:after,.vjs-ios-skin.video-js .vjs-button.vjs-share-button:before{border-radius:50%}.vjs-ios-skin .vjs-big-play-button:before,.vjs-ios-skin .vjs-control:before{content:'';display:none}.vjs-ios-skin .vjs-big-play-button>.vjs-button-icon,.vjs-ios-skin .vjs-control>.vjs-button-icon{position:absolute;left:0;top:0;width:100%;height:100%;transition:transform 150ms;mask-size:100% 100%;mask-repeat:no-repeat;-webkit-mask-size:100% 100%;-webkit-mask-repeat:no-repeat}.vjs-ios-skin .vjs-big-play-button>.vjs-button-icon>svg,.vjs-ios-skin .vjs-control>.vjs-button-icon>svg{fill:rgba(255,255,255,.75);width:100%;height:100%}.vjs-ios-skin .vjs-big-play-button:hover>.vjs-button-icon>svg,.vjs-ios-skin .vjs-control:hover>.vjs-button-icon>svg{fill:rgba(255,255,255,.9)}.vjs-ios-skin .vjs-big-play-button:active>.vjs-button-icon,.vjs-ios-skin .vjs-control:active>.vjs-button-icon{transform:scale(0.89)}.vjs-ios-skin .vjs-big-play-button{position:absolute;left:50%;top:50%;width:60px;height:60px;transform:translate(-50%,-50%);transition:none}.vjs-ios-skin .vjs-big-play-button,.vjs-ios-skin .vjs-big-play-button:active,.vjs-ios-skin .vjs-big-play-button:focus,.vjs-ios-skin:hover .vjs-big-play-button{border:none;background:0 0}.vjs-ios-skin .vjs-big-play-button>.vjs-ios-background-tint,.vjs-ios-skin .vjs-big-play-button>.vjs-ios-background-tint>div{border-radius:50%}.vjs-ios-skin .vjs-big-play-button>.vjs-button-icon,.vjs-ios-skin .vjs-fullscreen-control>.vjs-button-icon,.vjs-ios-skin .vjs-play-control>.vjs-button-icon,.vjs-ios-skin .vjs-skip-button>.vjs-button-icon,.vjs-ios-skin .vjs-volume-menu-button>.vjs-button-icon{background-color:rgba(255,255,255,.55)}.vjs-ios-skin .vjs-big-play-button>.vjs-button-icon,.vjs-ios-skin .vjs-fullscreen-control>.vjs-button-icon,.vjs-ios-skin .vjs-volume-menu-button>.vjs-button-icon{transform:scale(0.4)}.vjs-ios-skin .vjs-big-play-button:active>.vjs-button-icon,.vjs-ios-skin .vjs-fullscreen-control:active>.vjs-button-icon,.vjs-ios-skin .vjs-volume-menu-button:active>.vjs-button-icon{transform:scale(0.4) scale(0.88888889)}.vjs-ios-skin .vjs-big-play-button>.vjs-button-icon,.vjs-ios-skin .vjs-play-control>.vjs-button-icon{background-color:rgba(255,255,255,.75);mask-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTFweCIgaGVpZ2h0PSIxM3B4IiB2aWV3Qm94PSIwIDAgMTEgMTMiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQzLjEgKDM5MDEyKSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5fQXNzZXRzL0lubGluZS9QbGF5PC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGRlZnM+PC9kZWZzPgogICAgPGcgaWQ9Ik1lZGlhLUNvbnRyb2wtU3ltYm9scyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9Il9Bc3NldHMvSW5saW5lL1BsYXkiIGZpbGw9IiMwMDAwMDAiPgogICAgICAgICAgICA8cGF0aCBkPSJNMCwwLjYwNTA2ODY5MiBDMCwwLjA1ODE3MzcxMjEgMC4zODI1MTY0ODgsLTAuMTU2MTA0Nzg5IDAuODY0MTIyNjUsMC4xMzIzMDE4ODcgTDEwLjYzMjU5ODUsNS45ODIwODkyOCBDMTEuMTA5ODQwMyw2LjI2Nzg4MjM3IDExLjExNDIwNDcsNi43Mjg2MTkxMyAxMC42MzI1OTg1LDcuMDE3MDEwOTcgTDAuODY0MTIyNjUsMTIuODY2NDk3NSBDMC4zODY4ODA4ODksMTMuMTUyMjc1OSAwLDEyLjk0MTQxNjYgMCwxMi4zOTM3MDQxIEwwLDAuNjA1MDY4NjkyIFoiIGlkPSJSZWN0YW5nbGUiPjwvcGF0aD4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTFweCIgaGVpZ2h0PSIxM3B4IiB2aWV3Qm94PSIwIDAgMTEgMTMiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQzLjEgKDM5MDEyKSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5fQXNzZXRzL0lubGluZS9QbGF5PC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGRlZnM+PC9kZWZzPgogICAgPGcgaWQ9Ik1lZGlhLUNvbnRyb2wtU3ltYm9scyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9Il9Bc3NldHMvSW5saW5lL1BsYXkiIGZpbGw9IiMwMDAwMDAiPgogICAgICAgICAgICA8cGF0aCBkPSJNMCwwLjYwNTA2ODY5MiBDMCwwLjA1ODE3MzcxMjEgMC4zODI1MTY0ODgsLTAuMTU2MTA0Nzg5IDAuODY0MTIyNjUsMC4xMzIzMDE4ODcgTDEwLjYzMjU5ODUsNS45ODIwODkyOCBDMTEuMTA5ODQwMyw2LjI2Nzg4MjM3IDExLjExNDIwNDcsNi43Mjg2MTkxMyAxMC42MzI1OTg1LDcuMDE3MDEwOTcgTDAuODY0MTIyNjUsMTIuODY2NDk3NSBDMC4zODY4ODA4ODksMTMuMTUyMjc1OSAwLDEyLjk0MTQxNjYgMCwxMi4zOTM3MDQxIEwwLDAuNjA1MDY4NjkyIFoiIGlkPSJSZWN0YW5nbGUiPjwvcGF0aD4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==)}.vjs-ios-skin .vjs-big-play-button>.vjs-button-icon{left:3px}.vjs-ios-skin .vjs-play-control.vjs-playing>.vjs-button-icon{mask-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTFweCIgaGVpZ2h0PSIxM3B4IiB2aWV3Qm94PSIwIDAgMTEgMTMiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQzLjEgKDM5MDEyKSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5fQXNzZXRzL0lubGluZS9QYXVzZTwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPjwvZGVmcz4KICAgIDxnIGlkPSJNZWRpYS1Db250cm9sLVN5bWJvbHMiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSJfQXNzZXRzL0lubGluZS9QYXVzZSIgZmlsbD0iIzAwMDAwMCI+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik03LDAuOTk4NTAyMzI5IEM3LDAuNDQ3MDQ0NzIgNy40NDMzNTMxOCwwIDguMDA5MzY4OSwwIEw5Ljk5MDYzMTEsMCBDMTAuNTQ4MDkwMiwwIDExLDAuNDQ3NDg5NDI2IDExLDAuOTk4NTAyMzI5IEwxMSwxMi4wMDE0OTc3IEMxMSwxMi41NTI5NTUzIDEwLjU1NjY0NjgsMTMgOS45OTA2MzExLDEzIEw4LjAwOTM2ODksMTMgQzcuNDUxOTA5ODUsMTMgNywxMi41NTI1MTA2IDcsMTIuMDAxNDk3NyBMNywwLjk5ODUwMjMyOSBaIE0wLDAuOTk4NTAyMzI5IEMwLDAuNDQ3MDQ0NzIgMC40NDMzNTMxNzYsMCAxLjAwOTM2ODksMCBMMi45OTA2MzExLDAgQzMuNTQ4MDkwMTUsMCA0LDAuNDQ3NDg5NDI2IDQsMC45OTg1MDIzMjkgTDQsMTIuMDAxNDk3NyBDNCwxMi41NTI5NTUzIDMuNTU2NjQ2ODIsMTMgMi45OTA2MzExLDEzIEwxLjAwOTM2ODksMTMgQzAuNDUxOTA5ODQ4LDEzIDAsMTIuNTUyNTEwNiAwLDEyLjAwMTQ5NzcgTDAsMC45OTg1MDIzMjkgWiIgaWQ9IkNvbWJpbmVkLVNoYXBlIj48L3BhdGg+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTFweCIgaGVpZ2h0PSIxM3B4IiB2aWV3Qm94PSIwIDAgMTEgMTMiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQzLjEgKDM5MDEyKSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5fQXNzZXRzL0lubGluZS9QYXVzZTwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxkZWZzPjwvZGVmcz4KICAgIDxnIGlkPSJNZWRpYS1Db250cm9sLVN5bWJvbHMiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSJfQXNzZXRzL0lubGluZS9QYXVzZSIgZmlsbD0iIzAwMDAwMCI+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik03LDAuOTk4NTAyMzI5IEM3LDAuNDQ3MDQ0NzIgNy40NDMzNTMxOCwwIDguMDA5MzY4OSwwIEw5Ljk5MDYzMTEsMCBDMTAuNTQ4MDkwMiwwIDExLDAuNDQ3NDg5NDI2IDExLDAuOTk4NTAyMzI5IEwxMSwxMi4wMDE0OTc3IEMxMSwxMi41NTI5NTUzIDEwLjU1NjY0NjgsMTMgOS45OTA2MzExLDEzIEw4LjAwOTM2ODksMTMgQzcuNDUxOTA5ODUsMTMgNywxMi41NTI1MTA2IDcsMTIuMDAxNDk3NyBMNywwLjk5ODUwMjMyOSBaIE0wLDAuOTk4NTAyMzI5IEMwLDAuNDQ3MDQ0NzIgMC40NDMzNTMxNzYsMCAxLjAwOTM2ODksMCBMMi45OTA2MzExLDAgQzMuNTQ4MDkwMTUsMCA0LDAuNDQ3NDg5NDI2IDQsMC45OTg1MDIzMjkgTDQsMTIuMDAxNDk3NyBDNCwxMi41NTI5NTUzIDMuNTU2NjQ2ODIsMTMgMi45OTA2MzExLDEzIEwxLjAwOTM2ODksMTMgQzAuNDUxOTA5ODQ4LDEzIDAsMTIuNTUyNTEwNiAwLDEyLjAwMTQ5NzcgTDAsMC45OTg1MDIzMjkgWiIgaWQ9IkNvbWJpbmVkLVNoYXBlIj48L3BhdGg+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4=)}.vjs-ios-skin .vjs-live-control,.vjs-ios-skin .vjs-play-control,.vjs-ios-skin .vjs-skip-button{box-sizing:content-box;padding:0 8px}.vjs-ios-skin .vjs-play-control,.vjs-ios-skin .vjs-skip-button{cursor:pointer}.vjs-ios-skin .vjs-play-control>.vjs-button-icon,.vjs-ios-skin .vjs-skip-button>.vjs-button-icon{position:relative;height:31px}.vjs-ios-skin .vjs-play-control,.vjs-ios-skin .vjs-play-control>.vjs-button-icon{width:11px}.vjs-ios-skin .vjs-skip-button,.vjs-ios-skin .vjs-skip-button>.vjs-button-icon{width:13px}.vjs-ios-skin .vjs-skip-forward>.vjs-button-icon{mask-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTNweCIgaGVpZ2h0PSIxN3B4IiB2aWV3Qm94PSIwIDAgMTMgMTciIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQzLjIgKDM5MDY5KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5fQXNzZXRzL0lubGluZS9Ta2lwRm9yd2FyZDE1PC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGRlZnM+PC9kZWZzPgogICAgPGcgaWQ9Ik1lZGlhLUNvbnRyb2wtU3ltYm9scyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9Il9Bc3NldHMvSW5saW5lL1NraXBGb3J3YXJkMTUiIGZpbGw9IiMwMDAwMDAiPgogICAgICAgICAgICA8cGF0aCBkPSJNNC4wMTE0ODQzOCwxMSBMNS4wNDM3MTA5NSwxMSBMNS4wNDM3MTA5NSw2LjA2Nzg3MTA5IEw0LjAxNDkwMjM1LDYuMDY3ODcxMDkgTDIuNzQwMDAwMDEsNi45NDYyODkwNiBMMi43NDAwMDAwMSw3LjkwNjczODI4IEwzLjk0OTk2MDk1LDcuMDcyNzUzOTEgTDQuMDExNDg0MzgsNy4wNzI3NTM5MSBMNC4wMTE0ODQzOCwxMSBaIE04LjAxMzY5MTQzLDExLjEyNjQ2NDggQzkuMTQ1MDM5MDgsMTEuMTI2NDY0OCA5LjkwMDQxMDE4LDEwLjQxMjEwOTQgOS45MDA0MTAxOCw5LjM2NjIxMDk0IEM5LjkwMDQxMDE4LDguMzk4OTI1NzggOS4yMzczMjQyNCw3LjcxMTkxNDA2IDguMzAwODAwOCw3LjcxMTkxNDA2IEM3LjgxMjAzMTI3LDcuNzExOTE0MDYgNy40NjMzOTg0Niw3Ljg5MzA2NjQxIDcuMjU0OTAyMzYsOC4xOTM4NDc2NiBMNy4xOTMzNzg5Myw4LjE5Mzg0NzY2IEw3LjMwMjc1MzkzLDYuOTI1NzgxMjUgTDkuNjAzMDQ2ODksNi45MjU3ODEyNSBMOS42MDMwNDY4OSw2LjA2Nzg3MTA5IEw2LjUwMjk0OTI0LDYuMDY3ODcxMDkgTDYuMjU2ODU1NDksOC45Mjg3MTA5NCBMNy4xNzk3MDcwNSw4LjkyODcxMDk0IEM3LjM1MDYwNTQ5LDguNjUxODU1NDcgNy42NDExMzI4Myw4LjQ4NDM3NSA4LjAyMzk0NTMzLDguNDg0Mzc1IEM4LjU0Njg5NDU1LDguNDg0Mzc1IDguOTE2MDM1MTgsOC44NDY2Nzk2OSA4LjkxNjAzNTE4LDkuMzkwMTM2NzIgQzguOTE2MDM1MTgsOS45MjY3NTc4MSA4LjU1MDMxMjUyLDEwLjI4MjIyNjYgOC4wMjA1MjczNiwxMC4yODIyMjY2IEM3LjU1MjI2NTY0LDEwLjI4MjIyNjYgNy4xODk5NjA5NiwxMC4wMDg3ODkxIDcuMTMxODU1NDksOS41OTg2MzI4MSBMNi4xNzgyNDIyMSw5LjU5ODYzMjgxIEM2LjIwOTAwMzkzLDEwLjQ4MDQ2ODggNi45NDA0NDkyNCwxMS4xMjY0NjQ4IDguMDEzNjkxNDMsMTEuMTI2NDY0OCBaIiBpZD0iMTUiPjwvcGF0aD4KICAgICAgICAgICAgPHBhdGggZD0iTTUsMyBMNy41NTY2NDY4Miw0LjI3ODMyMzQxIEM3LjgwNzA3MTY5LDQuNDAzNTM1ODQgOCw0LjI4MDQwNTI5IDgsNC4wMDk1MjE0OCBMOCwzLjIwNzAzMjQ5IEMxMC4zMDg1MDQ4LDMuODU5OTYxMzggMTIsNS45ODI0NDU3NSAxMiw4LjUgQzEyLDExLjUzNzU2NjEgOS41Mzc1NjYxMiwxNCA2LjUsMTQgQzMuNDYyNDMzODgsMTQgMSwxMS41Mzc1NjYxIDEsOC41IEMxLDguMzMxNDU1MTUgMS4wMDc1ODEzMSw4LjE2NDY4MTAyIDEuMDIyNDIxNTEsOCBMMC4wMTg5NDQ4MTY3LDggQzAuMDA2MzkxNDYzMDIsOC4xNjUwMjA4MSAxLjMxODM4OTg0ZS0xNiw4LjMzMTc2NCAxLjMxODM4OTg0ZS0xNiw4LjUgQzEuMzE4Mzg5ODRlLTE2LDEyLjA4OTg1MDkgMi45MTAxNDkxMywxNSA2LjUsMTUgQzEwLjA4OTg1MDksMTUgMTMsMTIuMDg5ODUwOSAxMyw4LjUgQzEzLDUuNDI2NDE4MzYgMTAuODY2NywyLjg1MTA5NTY0IDgsMi4xNzM5MzE4NCBMOCwwLjk5MDQ3ODUxNiBDOCwwLjcxNTA1NzM3MyA3LjgwMTUwNDAyLDAuNTk5MjQ3OTg5IDcuNTU2NjQ2ODIsMC43MjE2NzY1ODggTDUsMiBMNSwwLjk5MDQ3ODUxNiBDNSwwLjcxNTA1NzM3MyA0LjgwMTUwNDAyLDAuNTk5MjQ3OTg5IDQuNTU2NjQ2ODIsMC43MjE2NzY1ODggTDEuNDQzMzUzMTgsMi4yNzgzMjM0MSBDMS4xOTI5MjgzMSwyLjQwMzUzNTg0IDEuMTk4NDk1OTgsMi41OTkyNDc5OSAxLjQ0MzM1MzE4LDIuNzIxNjc2NTkgTDQuNTU2NjQ2ODIsNC4yNzgzMjM0MSBDNC44MDcwNzE2OSw0LjQwMzUzNTg0IDUsNC4yODA0MDUyOSA1LDQuMDA5NTIxNDggTDUsMyBaIiBpZD0iQ29tYmluZWQtU2hhcGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYuNTAwMDAwLCA3LjgzNjUzMikgc2NhbGUoLTEsIDEpIHRyYW5zbGF0ZSgtNi41MDAwMDAsIC03LjgzNjUzMikgIj48L3BhdGg+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTNweCIgaGVpZ2h0PSIxN3B4IiB2aWV3Qm94PSIwIDAgMTMgMTciIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQzLjIgKDM5MDY5KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5fQXNzZXRzL0lubGluZS9Ta2lwRm9yd2FyZDE1PC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGRlZnM+PC9kZWZzPgogICAgPGcgaWQ9Ik1lZGlhLUNvbnRyb2wtU3ltYm9scyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9Il9Bc3NldHMvSW5saW5lL1NraXBGb3J3YXJkMTUiIGZpbGw9IiMwMDAwMDAiPgogICAgICAgICAgICA8cGF0aCBkPSJNNC4wMTE0ODQzOCwxMSBMNS4wNDM3MTA5NSwxMSBMNS4wNDM3MTA5NSw2LjA2Nzg3MTA5IEw0LjAxNDkwMjM1LDYuMDY3ODcxMDkgTDIuNzQwMDAwMDEsNi45NDYyODkwNiBMMi43NDAwMDAwMSw3LjkwNjczODI4IEwzLjk0OTk2MDk1LDcuMDcyNzUzOTEgTDQuMDExNDg0MzgsNy4wNzI3NTM5MSBMNC4wMTE0ODQzOCwxMSBaIE04LjAxMzY5MTQzLDExLjEyNjQ2NDggQzkuMTQ1MDM5MDgsMTEuMTI2NDY0OCA5LjkwMDQxMDE4LDEwLjQxMjEwOTQgOS45MDA0MTAxOCw5LjM2NjIxMDk0IEM5LjkwMDQxMDE4LDguMzk4OTI1NzggOS4yMzczMjQyNCw3LjcxMTkxNDA2IDguMzAwODAwOCw3LjcxMTkxNDA2IEM3LjgxMjAzMTI3LDcuNzExOTE0MDYgNy40NjMzOTg0Niw3Ljg5MzA2NjQxIDcuMjU0OTAyMzYsOC4xOTM4NDc2NiBMNy4xOTMzNzg5Myw4LjE5Mzg0NzY2IEw3LjMwMjc1MzkzLDYuOTI1NzgxMjUgTDkuNjAzMDQ2ODksNi45MjU3ODEyNSBMOS42MDMwNDY4OSw2LjA2Nzg3MTA5IEw2LjUwMjk0OTI0LDYuMDY3ODcxMDkgTDYuMjU2ODU1NDksOC45Mjg3MTA5NCBMNy4xNzk3MDcwNSw4LjkyODcxMDk0IEM3LjM1MDYwNTQ5LDguNjUxODU1NDcgNy42NDExMzI4Myw4LjQ4NDM3NSA4LjAyMzk0NTMzLDguNDg0Mzc1IEM4LjU0Njg5NDU1LDguNDg0Mzc1IDguOTE2MDM1MTgsOC44NDY2Nzk2OSA4LjkxNjAzNTE4LDkuMzkwMTM2NzIgQzguOTE2MDM1MTgsOS45MjY3NTc4MSA4LjU1MDMxMjUyLDEwLjI4MjIyNjYgOC4wMjA1MjczNiwxMC4yODIyMjY2IEM3LjU1MjI2NTY0LDEwLjI4MjIyNjYgNy4xODk5NjA5NiwxMC4wMDg3ODkxIDcuMTMxODU1NDksOS41OTg2MzI4MSBMNi4xNzgyNDIyMSw5LjU5ODYzMjgxIEM2LjIwOTAwMzkzLDEwLjQ4MDQ2ODggNi45NDA0NDkyNCwxMS4xMjY0NjQ4IDguMDEzNjkxNDMsMTEuMTI2NDY0OCBaIiBpZD0iMTUiPjwvcGF0aD4KICAgICAgICAgICAgPHBhdGggZD0iTTUsMyBMNy41NTY2NDY4Miw0LjI3ODMyMzQxIEM3LjgwNzA3MTY5LDQuNDAzNTM1ODQgOCw0LjI4MDQwNTI5IDgsNC4wMDk1MjE0OCBMOCwzLjIwNzAzMjQ5IEMxMC4zMDg1MDQ4LDMuODU5OTYxMzggMTIsNS45ODI0NDU3NSAxMiw4LjUgQzEyLDExLjUzNzU2NjEgOS41Mzc1NjYxMiwxNCA2LjUsMTQgQzMuNDYyNDMzODgsMTQgMSwxMS41Mzc1NjYxIDEsOC41IEMxLDguMzMxNDU1MTUgMS4wMDc1ODEzMSw4LjE2NDY4MTAyIDEuMDIyNDIxNTEsOCBMMC4wMTg5NDQ4MTY3LDggQzAuMDA2MzkxNDYzMDIsOC4xNjUwMjA4MSAxLjMxODM4OTg0ZS0xNiw4LjMzMTc2NCAxLjMxODM4OTg0ZS0xNiw4LjUgQzEuMzE4Mzg5ODRlLTE2LDEyLjA4OTg1MDkgMi45MTAxNDkxMywxNSA2LjUsMTUgQzEwLjA4OTg1MDksMTUgMTMsMTIuMDg5ODUwOSAxMyw4LjUgQzEzLDUuNDI2NDE4MzYgMTAuODY2NywyLjg1MTA5NTY0IDgsMi4xNzM5MzE4NCBMOCwwLjk5MDQ3ODUxNiBDOCwwLjcxNTA1NzM3MyA3LjgwMTUwNDAyLDAuNTk5MjQ3OTg5IDcuNTU2NjQ2ODIsMC43MjE2NzY1ODggTDUsMiBMNSwwLjk5MDQ3ODUxNiBDNSwwLjcxNTA1NzM3MyA0LjgwMTUwNDAyLDAuNTk5MjQ3OTg5IDQuNTU2NjQ2ODIsMC43MjE2NzY1ODggTDEuNDQzMzUzMTgsMi4yNzgzMjM0MSBDMS4xOTI5MjgzMSwyLjQwMzUzNTg0IDEuMTk4NDk1OTgsMi41OTkyNDc5OSAxLjQ0MzM1MzE4LDIuNzIxNjc2NTkgTDQuNTU2NjQ2ODIsNC4yNzgzMjM0MSBDNC44MDcwNzE2OSw0LjQwMzUzNTg0IDUsNC4yODA0MDUyOSA1LDQuMDA5NTIxNDggTDUsMyBaIiBpZD0iQ29tYmluZWQtU2hhcGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYuNTAwMDAwLCA3LjgzNjUzMikgc2NhbGUoLTEsIDEpIHRyYW5zbGF0ZSgtNi41MDAwMDAsIC03LjgzNjUzMikgIj48L3BhdGg+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4=)}.vjs-ios-skin .vjs-skip-back>.vjs-button-icon{mask-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTNweCIgaGVpZ2h0PSIxN3B4IiB2aWV3Qm94PSIwIDAgMTMgMTciIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQzLjIgKDM5MDY5KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5fQXNzZXRzL0lubGluZS9Ta2lwQmFjazE1PC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGRlZnM+PC9kZWZzPgogICAgPGcgaWQ9Ik1lZGlhLUNvbnRyb2wtU3ltYm9scyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9Il9Bc3NldHMvSW5saW5lL1NraXBCYWNrMTUiIGZpbGw9IiMwMDAwMDAiPgogICAgICAgICAgICA8cGF0aCBkPSJNNC4wMjE0ODQzOCwxMSBMNS4wNTM3MTA5NCwxMSBMNS4wNTM3MTA5NCw2LjA2Nzg3MTA5IEw0LjAyNDkwMjM0LDYuMDY3ODcxMDkgTDIuNzUsNi45NDYyODkwNiBMMi43NSw3LjkwNjczODI4IEwzLjk1OTk2MDk0LDcuMDcyNzUzOTEgTDQuMDIxNDg0MzgsNy4wNzI3NTM5MSBMNC4wMjE0ODQzOCwxMSBaIE04LjAyMzY5MTQyLDExLjEyNjQ2NDggQzkuMTU1MDM5MDcsMTEuMTI2NDY0OCA5LjkxMDQxMDE3LDEwLjQxMjEwOTQgOS45MTA0MTAxNyw5LjM2NjIxMDk0IEM5LjkxMDQxMDE3LDguMzk4OTI1NzggOS4yNDczMjQyMyw3LjcxMTkxNDA2IDguMzEwODAwNzksNy43MTE5MTQwNiBDNy44MjIwMzEyNiw3LjcxMTkxNDA2IDcuNDczMzk4NDUsNy44OTMwNjY0MSA3LjI2NDkwMjM1LDguMTkzODQ3NjYgTDcuMjAzMzc4OTIsOC4xOTM4NDc2NiBMNy4zMTI3NTM5Miw2LjkyNTc4MTI1IEw5LjYxMzA0Njg4LDYuOTI1NzgxMjUgTDkuNjEzMDQ2ODgsNi4wNjc4NzEwOSBMNi41MTI5NDkyMyw2LjA2Nzg3MTA5IEw2LjI2Njg1NTQ4LDguOTI4NzEwOTQgTDcuMTg5NzA3MDQsOC45Mjg3MTA5NCBDNy4zNjA2MDU0OCw4LjY1MTg1NTQ3IDcuNjUxMTMyODIsOC40ODQzNzUgOC4wMzM5NDUzMiw4LjQ4NDM3NSBDOC41NTY4OTQ1NCw4LjQ4NDM3NSA4LjkyNjAzNTE3LDguODQ2Njc5NjkgOC45MjYwMzUxNyw5LjM5MDEzNjcyIEM4LjkyNjAzNTE3LDkuOTI2NzU3ODEgOC41NjAzMTI1MSwxMC4yODIyMjY2IDguMDMwNTI3MzUsMTAuMjgyMjI2NiBDNy41NjIyNjU2MywxMC4yODIyMjY2IDcuMTk5OTYwOTUsMTAuMDA4Nzg5MSA3LjE0MTg1NTQ4LDkuNTk4NjMyODEgTDYuMTg4MjQyMiw5LjU5ODYzMjgxIEM2LjIxOTAwMzkyLDEwLjQ4MDQ2ODggNi45NTA0NDkyMywxMS4xMjY0NjQ4IDguMDIzNjkxNDIsMTEuMTI2NDY0OCBaIiBpZD0iMTUiPjwvcGF0aD4KICAgICAgICAgICAgPHBhdGggZD0iTTUsMyBMNy41NTY2NDY4Miw0LjI3ODMyMzQxIEM3LjgwNzA3MTY5LDQuNDAzNTM1ODQgOCw0LjI4MDQwNTI5IDgsNC4wMDk1MjE0OCBMOCwzLjIwNzAzMjQ5IEMxMC4zMDg1MDQ4LDMuODU5OTYxMzggMTIsNS45ODI0NDU3NSAxMiw4LjUgQzEyLDExLjUzNzU2NjEgOS41Mzc1NjYxMiwxNCA2LjUsMTQgQzMuNDYyNDMzODgsMTQgMSwxMS41Mzc1NjYxIDEsOC41IEMxLDguMzMxNDU1MTUgMS4wMDc1ODEzMSw4LjE2NDY4MTAyIDEuMDIyNDIxNTEsOCBMMC4wMTg5NDQ4MTY3LDggQzAuMDA2MzkxNDYzMDIsOC4xNjUwMjA4MSAxLjMxODM4OTg0ZS0xNiw4LjMzMTc2NCAxLjMxODM4OTg0ZS0xNiw4LjUgQzEuMzE4Mzg5ODRlLTE2LDEyLjA4OTg1MDkgMi45MTAxNDkxMywxNSA2LjUsMTUgQzEwLjA4OTg1MDksMTUgMTMsMTIuMDg5ODUwOSAxMyw4LjUgQzEzLDUuNDI2NDE4MzYgMTAuODY2NywyLjg1MTA5NTY0IDgsMi4xNzM5MzE4NCBMOCwwLjk5MDQ3ODUxNiBDOCwwLjcxNTA1NzM3MyA3LjgwMTUwNDAyLDAuNTk5MjQ3OTg5IDcuNTU2NjQ2ODIsMC43MjE2NzY1ODggTDUsMiBMNSwwLjk5MDQ3ODUxNiBDNSwwLjcxNTA1NzM3MyA0LjgwMTUwNDAyLDAuNTk5MjQ3OTg5IDQuNTU2NjQ2ODIsMC43MjE2NzY1ODggTDEuNDQzMzUzMTgsMi4yNzgzMjM0MSBDMS4xOTI5MjgzMSwyLjQwMzUzNTg0IDEuMTk4NDk1OTgsMi41OTkyNDc5OSAxLjQ0MzM1MzE4LDIuNzIxNjc2NTkgTDQuNTU2NjQ2ODIsNC4yNzgzMjM0MSBDNC44MDcwNzE2OSw0LjQwMzUzNTg0IDUsNC4yODA0MDUyOSA1LDQuMDA5NTIxNDggTDUsMyBaIiBpZD0iQ29tYmluZWQtU2hhcGUiPjwvcGF0aD4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTNweCIgaGVpZ2h0PSIxN3B4IiB2aWV3Qm94PSIwIDAgMTMgMTciIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQzLjIgKDM5MDY5KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5fQXNzZXRzL0lubGluZS9Ta2lwQmFjazE1PC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGRlZnM+PC9kZWZzPgogICAgPGcgaWQ9Ik1lZGlhLUNvbnRyb2wtU3ltYm9scyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9Il9Bc3NldHMvSW5saW5lL1NraXBCYWNrMTUiIGZpbGw9IiMwMDAwMDAiPgogICAgICAgICAgICA8cGF0aCBkPSJNNC4wMjE0ODQzOCwxMSBMNS4wNTM3MTA5NCwxMSBMNS4wNTM3MTA5NCw2LjA2Nzg3MTA5IEw0LjAyNDkwMjM0LDYuMDY3ODcxMDkgTDIuNzUsNi45NDYyODkwNiBMMi43NSw3LjkwNjczODI4IEwzLjk1OTk2MDk0LDcuMDcyNzUzOTEgTDQuMDIxNDg0MzgsNy4wNzI3NTM5MSBMNC4wMjE0ODQzOCwxMSBaIE04LjAyMzY5MTQyLDExLjEyNjQ2NDggQzkuMTU1MDM5MDcsMTEuMTI2NDY0OCA5LjkxMDQxMDE3LDEwLjQxMjEwOTQgOS45MTA0MTAxNyw5LjM2NjIxMDk0IEM5LjkxMDQxMDE3LDguMzk4OTI1NzggOS4yNDczMjQyMyw3LjcxMTkxNDA2IDguMzEwODAwNzksNy43MTE5MTQwNiBDNy44MjIwMzEyNiw3LjcxMTkxNDA2IDcuNDczMzk4NDUsNy44OTMwNjY0MSA3LjI2NDkwMjM1LDguMTkzODQ3NjYgTDcuMjAzMzc4OTIsOC4xOTM4NDc2NiBMNy4zMTI3NTM5Miw2LjkyNTc4MTI1IEw5LjYxMzA0Njg4LDYuOTI1NzgxMjUgTDkuNjEzMDQ2ODgsNi4wNjc4NzEwOSBMNi41MTI5NDkyMyw2LjA2Nzg3MTA5IEw2LjI2Njg1NTQ4LDguOTI4NzEwOTQgTDcuMTg5NzA3MDQsOC45Mjg3MTA5NCBDNy4zNjA2MDU0OCw4LjY1MTg1NTQ3IDcuNjUxMTMyODIsOC40ODQzNzUgOC4wMzM5NDUzMiw4LjQ4NDM3NSBDOC41NTY4OTQ1NCw4LjQ4NDM3NSA4LjkyNjAzNTE3LDguODQ2Njc5NjkgOC45MjYwMzUxNyw5LjM5MDEzNjcyIEM4LjkyNjAzNTE3LDkuOTI2NzU3ODEgOC41NjAzMTI1MSwxMC4yODIyMjY2IDguMDMwNTI3MzUsMTAuMjgyMjI2NiBDNy41NjIyNjU2MywxMC4yODIyMjY2IDcuMTk5OTYwOTUsMTAuMDA4Nzg5MSA3LjE0MTg1NTQ4LDkuNTk4NjMyODEgTDYuMTg4MjQyMiw5LjU5ODYzMjgxIEM2LjIxOTAwMzkyLDEwLjQ4MDQ2ODggNi45NTA0NDkyMywxMS4xMjY0NjQ4IDguMDIzNjkxNDIsMTEuMTI2NDY0OCBaIiBpZD0iMTUiPjwvcGF0aD4KICAgICAgICAgICAgPHBhdGggZD0iTTUsMyBMNy41NTY2NDY4Miw0LjI3ODMyMzQxIEM3LjgwNzA3MTY5LDQuNDAzNTM1ODQgOCw0LjI4MDQwNTI5IDgsNC4wMDk1MjE0OCBMOCwzLjIwNzAzMjQ5IEMxMC4zMDg1MDQ4LDMuODU5OTYxMzggMTIsNS45ODI0NDU3NSAxMiw4LjUgQzEyLDExLjUzNzU2NjEgOS41Mzc1NjYxMiwxNCA2LjUsMTQgQzMuNDYyNDMzODgsMTQgMSwxMS41Mzc1NjYxIDEsOC41IEMxLDguMzMxNDU1MTUgMS4wMDc1ODEzMSw4LjE2NDY4MTAyIDEuMDIyNDIxNTEsOCBMMC4wMTg5NDQ4MTY3LDggQzAuMDA2MzkxNDYzMDIsOC4xNjUwMjA4MSAxLjMxODM4OTg0ZS0xNiw4LjMzMTc2NCAxLjMxODM4OTg0ZS0xNiw4LjUgQzEuMzE4Mzg5ODRlLTE2LDEyLjA4OTg1MDkgMi45MTAxNDkxMywxNSA2LjUsMTUgQzEwLjA4OTg1MDksMTUgMTMsMTIuMDg5ODUwOSAxMyw4LjUgQzEzLDUuNDI2NDE4MzYgMTAuODY2NywyLjg1MTA5NTY0IDgsMi4xNzM5MzE4NCBMOCwwLjk5MDQ3ODUxNiBDOCwwLjcxNTA1NzM3MyA3LjgwMTUwNDAyLDAuNTk5MjQ3OTg5IDcuNTU2NjQ2ODIsMC43MjE2NzY1ODggTDUsMiBMNSwwLjk5MDQ3ODUxNiBDNSwwLjcxNTA1NzM3MyA0LjgwMTUwNDAyLDAuNTk5MjQ3OTg5IDQuNTU2NjQ2ODIsMC43MjE2NzY1ODggTDEuNDQzMzUzMTgsMi4yNzgzMjM0MSBDMS4xOTI5MjgzMSwyLjQwMzUzNTg0IDEuMTk4NDk1OTgsMi41OTkyNDc5OSAxLjQ0MzM1MzE4LDIuNzIxNjc2NTkgTDQuNTU2NjQ2ODIsNC4yNzgzMjM0MSBDNC44MDcwNzE2OSw0LjQwMzUzNTg0IDUsNC4yODA0MDUyOSA1LDQuMDA5NTIxNDggTDUsMyBaIiBpZD0iQ29tYmluZWQtU2hhcGUiPjwvcGF0aD4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==)}.vjs-ios-skin .vjs-menu{background:0 0}.vjs-ios-skin .vjs-menu .vjs-menu-item,.vjs-ios-skin .vjs-menu li{height:31px;line-height:31px;padding-top:0;padding-bottom:0}.vjs-ios-skin .vjs-menu .vjs-menu-item.vjs-selected:focus,.vjs-ios-skin .vjs-menu .vjs-menu-item.vjs-selected:hover,.vjs-ios-skin .vjs-menu li:focus,.vjs-ios-skin .vjs-menu li:hover{background-color:rgba(255,255,255,.55)}.vjs-ios-skin .vjs-menu-item-link>.vjs-menu-link{display:block;color:inherit;text-decoration:inherit;text-transform:inherit;white-space:nowrap;border:none}.vjs-ios-skin .vjs-time-control{padding:0 6px;color:rgba(255,255,255,.55);font-size:12px;text-align:right}.vjs-ios-skin .vjs-current-time{padding-left:8px}.vjs-ios-skin .vjs-current-time-display{display:block;line-height:normal}.vjs-ios-skin.vjs-live .vjs-current-time,.vjs-ios-skin.vjs-live .vjs-duration,.vjs-ios-skin.vjs-live .vjs-remaining-time,.vjs-ios-skin.vjs-live .vjs-skip-button,.vjs-ios-skin.vjs-live .vjs-time-control,.vjs-ios-skin.vjs-live .vjs-time-divider,.vjs-ios-skin.vjs-live:not(.vjs-dvr) .vjs-progress-control{display:none}.vjs-ios-skin.vjs-live .vjs-live-display{line-height:normal}.vjs-ios-skin.vjs-live .vjs-live-display:before{display:inline-block;width:6px;height:6px;margin-right:4px;vertical-align:1px;background:#f51717;content:'';border-radius:50%}.vjs-ios-skin .vjs-volume-menu-button{top:0;right:0}.vjs-ios-skin .vjs-volume-menu-button,.vjs-ios-skin .vjs-volume-menu-button:focus,.vjs-ios-skin .vjs-volume-menu-button:hover{width:54px}.vjs-ios-skin .vjs-volume-menu-button:before{width:100%}.vjs-ios-skin .vjs-volume-menu-button>.vjs-menu{display:none}.vjs-ios-skin .vjs-volume-menu-button>.vjs-button-icon{mask-image:url(data:image/svg+xml;base64, );-webkit-mask-image:url(data:image/svg+xml;base64, )}.vjs-ios-skin .vjs-volume-menu-button.vjs-vol-0>.vjs-button-icon{mask-image:url(data:image/svg+xml;base64, );-webkit-mask-image:url(data:image/svg+xml;base64, )}.vjs-ios-skin .vjs-fullscreen-control{top:0;left:0;width:47px}.vjs-ios-skin .vjs-fullscreen-control>.vjs-button-icon{mask-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgMTUgMTUiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQzLjEgKDM5MDEyKSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5fQXNzZXRzL0lubGluZS9FbnRlckZ1bGxzY3JlZW48L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz48L2RlZnM+CiAgICA8ZyBpZD0iTWVkaWEtQ29udHJvbC1TeW1ib2xzIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0iX0Fzc2V0cy9JbmxpbmUvRW50ZXJGdWxsc2NyZWVuIiBmaWxsPSIjMDAwMDAwIj4KICAgICAgICAgICAgPHBhdGggZD0iTTQuNDQyMjYwNCw1LjUwMjg3ODgxIEwwLjIxNzc1MzE3OCwxLjI3ODM3MTU4IEMtMC4wNzMwNDMzOTgxLDAuOTg3NTc1MDA5IC0wLjA3MzM4NTczMSwwLjUxNjQ0MjkzNyAwLjIyMTU0OTQ4NSwwLjIyMTUwNzcyIEMwLjUxNDQ0MjcwNCwtMC4wNzEzODU0OTgzIDAuOTg1NzA0NDg3LC0wLjA3NDk5NzQ1MDIgMS4yNzg0MTMzNSwwLjIxNzcxMTQxMyBMNS41MTEyNjUxNiw0LjQ1MDU2MzIyIEw1LjUxMTI2NTE2LDIuMDQyMTI5MDYgQzUuNTExMjY1MTYsMS42MjkyNTcyNyA1Ljg0NDE2Mzc3LDEuMjk0NTU4NTUgNi4yNjEyNjUxNiwxLjI5NDU1ODU1IEM2LjY3NTQ3ODcyLDEuMjk0NTU4NTUgNy4wMTEyNjUxNiwxLjYzNTE0MDY5IDcuMDExMjY1MTYsMi4wNDIxMjkwNiBMNy4wMTEyNjUxNiw2LjIwOTM2OTYxIEM3LjAxNjYyMTM5LDYuMjg4Njc2OTMgNy4wMDkzMDI1OSw2LjM2ODc3NTQyIDYuOTg5MjY1MjgsNi40NDY0NTEzNyBDNi45MDg2MDM5NCw2Ljc3MTc5NjkgNi42MTUyNTk5OCw3LjAxMjg3ODggNi4yNjEyNjUxNiw3LjAxMjg3ODggQzYuMjE5NzI0MjEsNy4wMTI4Nzg4IDYuMTc4OTcyMDcsNy4wMDk0NTMyOCA2LjEzOTI2ODMzLDcuMDAyODc4ODEgTDIuMDQwNTE1NDIsNy4wMDI4Nzg4MSBDMS42Mjc2NDM2Miw3LjAwMjg3ODgxIDEuMjkyOTQ0OTEsNi42Njk5ODAxOSAxLjI5Mjk0NDkxLDYuMjUyODc4ODEgQzEuMjkyOTQ0OTEsNS44Mzg2NjUyNSAxLjYzMzUyNzA0LDUuNTAyODc4ODEgMi4wNDA1MTU0Miw1LjUwMjg3ODgxIEw0LjQ0MjI2MDQsNS41MDI4Nzg4MSBaIiBpZD0iQ29tYmluZWQtU2hhcGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDMuNTA2NDgxLCAzLjUwNjQzOSkgcm90YXRlKDE4MC4wMDAwMDApIHRyYW5zbGF0ZSgtMy41MDY0ODEsIC0zLjUwNjQzOSkgIj48L3BhdGg+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMi40NDIyNjA0LDEzLjUwMjg3ODggTDguMjE3NzUzMTgsOS4yNzgzNzE1OCBDNy45MjY5NTY2LDguOTg3NTc1MDEgNy45MjY2MTQyNyw4LjUxNjQ0Mjk0IDguMjIxNTQ5NDksOC4yMjE1MDc3MiBDOC41MTQ0NDI3LDcuOTI4NjE0NSA4Ljk4NTcwNDQ5LDcuOTI1MDAyNTUgOS4yNzg0MTMzNSw4LjIxNzcxMTQxIEwxMy41MTEyNjUyLDEyLjQ1MDU2MzIgTDEzLjUxMTI2NTIsMTAuMDQyMTI5MSBDMTMuNTExMjY1Miw5LjYyOTI1NzI3IDEzLjg0NDE2MzgsOS4yOTQ1NTg1NSAxNC4yNjEyNjUyLDkuMjk0NTU4NTUgQzE0LjY3NTQ3ODcsOS4yOTQ1NTg1NSAxNS4wMTEyNjUyLDkuNjM1MTQwNjkgMTUuMDExMjY1MiwxMC4wNDIxMjkxIEwxNS4wMTEyNjUyLDE0LjIwOTM2OTYgQzE1LjAxNjYyMTQsMTQuMjg4Njc2OSAxNS4wMDkzMDI2LDE0LjM2ODc3NTQgMTQuOTg5MjY1MywxNC40NDY0NTE0IEMxNC45MDg2MDM5LDE0Ljc3MTc5NjkgMTQuNjE1MjYsMTUuMDEyODc4OCAxNC4yNjEyNjUyLDE1LjAxMjg3ODggQzE0LjIxOTcyNDIsMTUuMDEyODc4OCAxNC4xNzg5NzIxLDE1LjAwOTQ1MzMgMTQuMTM5MjY4MywxNS4wMDI4Nzg4IEwxMC4wNDA1MTU0LDE1LjAwMjg3ODggQzkuNjI3NjQzNjIsMTUuMDAyODc4OCA5LjI5Mjk0NDkxLDE0LjY2OTk4MDIgOS4yOTI5NDQ5MSwxNC4yNTI4Nzg4IEM5LjI5Mjk0NDkxLDEzLjgzODY2NTIgOS42MzM1MjcwNCwxMy41MDI4Nzg4IDEwLjA0MDUxNTQsMTMuNTAyODc4OCBMMTIuNDQyMjYwNCwxMy41MDI4Nzg4IFoiIGlkPSJDb21iaW5lZC1TaGFwZSI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMTVweCIgaGVpZ2h0PSIxNXB4IiB2aWV3Qm94PSIwIDAgMTUgMTUiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQzLjEgKDM5MDEyKSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5fQXNzZXRzL0lubGluZS9FbnRlckZ1bGxzY3JlZW48L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz48L2RlZnM+CiAgICA8ZyBpZD0iTWVkaWEtQ29udHJvbC1TeW1ib2xzIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0iX0Fzc2V0cy9JbmxpbmUvRW50ZXJGdWxsc2NyZWVuIiBmaWxsPSIjMDAwMDAwIj4KICAgICAgICAgICAgPHBhdGggZD0iTTQuNDQyMjYwNCw1LjUwMjg3ODgxIEwwLjIxNzc1MzE3OCwxLjI3ODM3MTU4IEMtMC4wNzMwNDMzOTgxLDAuOTg3NTc1MDA5IC0wLjA3MzM4NTczMSwwLjUxNjQ0MjkzNyAwLjIyMTU0OTQ4NSwwLjIyMTUwNzcyIEMwLjUxNDQ0MjcwNCwtMC4wNzEzODU0OTgzIDAuOTg1NzA0NDg3LC0wLjA3NDk5NzQ1MDIgMS4yNzg0MTMzNSwwLjIxNzcxMTQxMyBMNS41MTEyNjUxNiw0LjQ1MDU2MzIyIEw1LjUxMTI2NTE2LDIuMDQyMTI5MDYgQzUuNTExMjY1MTYsMS42MjkyNTcyNyA1Ljg0NDE2Mzc3LDEuMjk0NTU4NTUgNi4yNjEyNjUxNiwxLjI5NDU1ODU1IEM2LjY3NTQ3ODcyLDEuMjk0NTU4NTUgNy4wMTEyNjUxNiwxLjYzNTE0MDY5IDcuMDExMjY1MTYsMi4wNDIxMjkwNiBMNy4wMTEyNjUxNiw2LjIwOTM2OTYxIEM3LjAxNjYyMTM5LDYuMjg4Njc2OTMgNy4wMDkzMDI1OSw2LjM2ODc3NTQyIDYuOTg5MjY1MjgsNi40NDY0NTEzNyBDNi45MDg2MDM5NCw2Ljc3MTc5NjkgNi42MTUyNTk5OCw3LjAxMjg3ODggNi4yNjEyNjUxNiw3LjAxMjg3ODggQzYuMjE5NzI0MjEsNy4wMTI4Nzg4IDYuMTc4OTcyMDcsNy4wMDk0NTMyOCA2LjEzOTI2ODMzLDcuMDAyODc4ODEgTDIuMDQwNTE1NDIsNy4wMDI4Nzg4MSBDMS42Mjc2NDM2Miw3LjAwMjg3ODgxIDEuMjkyOTQ0OTEsNi42Njk5ODAxOSAxLjI5Mjk0NDkxLDYuMjUyODc4ODEgQzEuMjkyOTQ0OTEsNS44Mzg2NjUyNSAxLjYzMzUyNzA0LDUuNTAyODc4ODEgMi4wNDA1MTU0Miw1LjUwMjg3ODgxIEw0LjQ0MjI2MDQsNS41MDI4Nzg4MSBaIiBpZD0iQ29tYmluZWQtU2hhcGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDMuNTA2NDgxLCAzLjUwNjQzOSkgcm90YXRlKDE4MC4wMDAwMDApIHRyYW5zbGF0ZSgtMy41MDY0ODEsIC0zLjUwNjQzOSkgIj48L3BhdGg+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMi40NDIyNjA0LDEzLjUwMjg3ODggTDguMjE3NzUzMTgsOS4yNzgzNzE1OCBDNy45MjY5NTY2LDguOTg3NTc1MDEgNy45MjY2MTQyNyw4LjUxNjQ0Mjk0IDguMjIxNTQ5NDksOC4yMjE1MDc3MiBDOC41MTQ0NDI3LDcuOTI4NjE0NSA4Ljk4NTcwNDQ5LDcuOTI1MDAyNTUgOS4yNzg0MTMzNSw4LjIxNzcxMTQxIEwxMy41MTEyNjUyLDEyLjQ1MDU2MzIgTDEzLjUxMTI2NTIsMTAuMDQyMTI5MSBDMTMuNTExMjY1Miw5LjYyOTI1NzI3IDEzLjg0NDE2MzgsOS4yOTQ1NTg1NSAxNC4yNjEyNjUyLDkuMjk0NTU4NTUgQzE0LjY3NTQ3ODcsOS4yOTQ1NTg1NSAxNS4wMTEyNjUyLDkuNjM1MTQwNjkgMTUuMDExMjY1MiwxMC4wNDIxMjkxIEwxNS4wMTEyNjUyLDE0LjIwOTM2OTYgQzE1LjAxNjYyMTQsMTQuMjg4Njc2OSAxNS4wMDkzMDI2LDE0LjM2ODc3NTQgMTQuOTg5MjY1MywxNC40NDY0NTE0IEMxNC45MDg2MDM5LDE0Ljc3MTc5NjkgMTQuNjE1MjYsMTUuMDEyODc4OCAxNC4yNjEyNjUyLDE1LjAxMjg3ODggQzE0LjIxOTcyNDIsMTUuMDEyODc4OCAxNC4xNzg5NzIxLDE1LjAwOTQ1MzMgMTQuMTM5MjY4MywxNS4wMDI4Nzg4IEwxMC4wNDA1MTU0LDE1LjAwMjg3ODggQzkuNjI3NjQzNjIsMTUuMDAyODc4OCA5LjI5Mjk0NDkxLDE0LjY2OTk4MDIgOS4yOTI5NDQ5MSwxNC4yNTI4Nzg4IEM5LjI5Mjk0NDkxLDEzLjgzODY2NTIgOS42MzM1MjcwNCwxMy41MDI4Nzg4IDEwLjA0MDUxNTQsMTMuNTAyODc4OCBMMTIuNDQyMjYwNCwxMy41MDI4Nzg4IFoiIGlkPSJDb21iaW5lZC1TaGFwZSI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+);transform:scale(0.5)}.vjs-ios-skin .vjs-fullscreen-control:active>.vjs-button-icon{transform:scale(0.5) scale(0.88888889)}.vjs-ios-skin .vjs-mouse-display,.vjs-ios-skin .vjs-mouse-display-tooltip{display:none!important}.vjs-ios-skin .vjs-live-control{flex:none;width:auto;font-size:12px}.vjs-ios-skin .vjs-live-display{margin:0}.vjs-ios-skin .vjs-slider{background-color:rgba(255,255,255,.1)}.vjs-ios-skin .vjs-progress-control:hover .vjs-progress-holder{font-size:100%}.vjs-ios-skin .vjs-progress-holder{margin:0}.vjs-ios-skin .vjs-progress-holder,.vjs-ios-skin .vjs-progress-holder>.vjs-load-progress,.vjs-ios-skin .vjs-progress-holder>.vjs-load-progress>div,.vjs-ios-skin .vjs-progress-holder>.vjs-play-progress,.vjs-ios-skin .vjs-progress-holder>.vjs-tooltip-progress-bar{height:5px;border-radius:4.5px}.vjs-ios-skin .vjs-load-progress,.vjs-ios-skin .vjs-load-progress>div{background-color:rgba(255,255,255,.08)}.vjs-ios-skin .vjs-play-progress{min-width:8px;background-color:rgba(255,255,255,.35)}.vjs-ios-skin .vjs-play-progress:before{top:-2px;right:-5px;width:9px;height:9px;border-radius:50%;background-color:#fff;transform:translateX(-50%)}.vjs-ios-skin .vjs-play-progress:after{content:'';display:none!important}.vjs-ios-skin.vjs-ad-playing .vjs-ios-background-tint{display:block!important}.vjs-ios-skin.vjs-ad-playing .vjs-control-bar .ima-countdown-div{left:0}.vjs-ios-skin.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-control-bar{padding:0 3px}.vjs-ios-skin.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-control-bar>.vjs-ios-background-tint{opacity:1!important;visibility:visible!important;height:10px;top:-5px}.vjs-ios-skin.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-current-time,.vjs-ios-skin.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-play-control,.vjs-ios-skin.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-remaining-time,.vjs-ios-skin.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-skip-button{display:none}.vjs-ios-skin .videoAdUiPreSkipContainer{bottom:27px;margin-right:6px}.vjs-ios-skin .videoAdUiPreSkipButton{background:rgba(255,255,255,.55);border-radius:8px}"; (_dereq_("browserify-css").createStyle(css, { "href": "src/css/videojs-hola-skin.css" }, { "insertAt": "bottom" })); module.exports = css;
},{"browserify-css":171}],404:[function(_dereq_,module,exports){
var css = ".ima-ad-container{top:0;position:absolute;display:block;width:100%;height:100%}.video-js.vjs-playing .bumpable-ima-ad-container{margin-top:-50px}.video-js.vjs-user-inactive.vjs-playing .bumpable-ima-ad-container{margin-top:0}.video-js.vjs-paused .bumpable-ima-ad-container,.video-js.vjs-playing:hover .bumpable-ima-ad-container,.video-js.vjs-user-active.vjs-playing .bumpable-ima-ad-container{margin-top:-50px}.vjs-ima-non-linear .vjs-big-play-button,.vjs-ima-non-linear .vjs-control-bar,.vjs-ima-non-linear .vjs-info-overlay,.vjs-ima-non-linear .vjs-menu.vjs-settings-menu,.vjs-ima-non-linear .vjs-modal-overlay{z-index:1112}.ima-controls-div{bottom:0;height:37px;position:absolute;overflow:hidden;display:none;opacity:1;background:-moz-linear-gradient(bottom,rgba(7,20,30,.7) 0,rgba(7,20,30,0) 100%);background:-webkit-gradient(linear,left bottom,left top,color-stop(0%,rgba(7,20,30,.7)),color-stop(100%,rgba(7,20,30,0)));background:-webkit-linear-gradient(bottom,rgba(7,20,30,.7) 0,rgba(7,20,30,0) 100%);background:-o-linear-gradient(bottom,rgba(7,20,30,.7) 0,rgba(7,20,30,0) 100%);background:-ms-linear-gradient(bottom,rgba(7,20,30,.7) 0,rgba(7,20,30,0) 100%);background:linear-gradient(to top,rgba(7,20,30,.7) 0,rgba(7,20,30,0) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#0007141E', endColorstr='#07141E', GradientType=0)}.ima-countdown-div{height:10px;color:#FFF;text-shadow:0 0 .2em #000;cursor:default}.ima-seek-bar-div{top:12px;height:3px;position:absolute;background:rgba(255,255,255,.4)}.ima-progress-div{width:0;height:3px;background-color:#ECC546}.ima-fullscreen-div,.ima-mute-div,.ima-play-pause-div,.ima-slider-div{width:35px;height:20px;top:11px;left:0;position:absolute;color:#CCC;font-size:1.5em;line-height:2;text-align:center;font-family:VideoJS;cursor:pointer}.ima-mute-div{left:auto;right:85px}.ima-slider-div{left:auto;right:35px;width:50px;height:10px;top:20px;background-color:#555}.ima-slider-level-div{width:100%;height:10px;background-color:#ECC546}.ima-fullscreen-div{left:auto;right:0}.ima-playing:before{content:\"\\00f103\"}.ima-paused:before{content:\"\\00f101\"}.ima-paused:hover:before,.ima-playing:hover:before{text-shadow:0 0 1em #fff}.ima-non-muted:before{content:\"\\00f107\"}.ima-muted:before{content:\"\\00f104\"}.ima-muted:hover:before,.ima-non-muted:hover:before{text-shadow:0 0 1em #fff}.ima-non-fullscreen:before{content:\"\\00f108\"}.ima-fullscreen:before{content:\"\\00f109\"}.ima-fullscreen:hover:before,.ima-non-fullscreen:hover:before{text-shadow:0 0 1em #fff}.video-js.vjs-ad-loading .vjs-control-bar,.video-js.vjs-ad-playing .vjs-control-bar{z-index:1112}.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control{pointer-events:none}.video-js.vjs-ad-playing .vjs-control-bar .vjs-mouse-display-tooltip,.video-js.vjs-ad-playing .vjs-control-bar .vjs-play-progress:before,.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control .vjs-load-progress,.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control .vjs-mouse-display,.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control:before,.video-js.vjs-ad-playing .vjs-control-bar>:not(.vjs-play-control):not(.vjs-volume-menu-button):not(.vjs-time-control):not(.vjs-progress-control):not(.vjs-spacer):not(.vjs-fullscreen-control):not(.vjs-gradient):not(.ima-countdown-div){display:none}.video-js.vjs-ad-playing .vjs-control-bar{transition:height .1s ease}.video-js.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-control-bar{opacity:1;height:0}.video-js.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-control-bar .vjs-progress-holder{margin:0}.video-js.vjs-ad-playing .vjs-control-bar>:not(.vjs-progress-control):not(.ima-countdown-div){transition:opacity .1s ease,visibility .1s ease}.video-js.vjs-ad-playing .vjs-control-bar>:not(.vjs-progress-control):not(.ima-countdown-div) div{transition:line-height .1s ease}.video-js.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-control-bar>:not(.vjs-progress-control):not(.ima-countdown-div){opacity:0;visibility:hidden;overflow:hidden}.video-js.vjs-ad-playing.vjs-user-inactive:not(.vjs-ad-paused) .vjs-control-bar>:not(.vjs-progress-control):not(.ima-countdown-div) div{line-height:.01em}.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control .vjs-thumbnail,.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control .vjs-thumbnail-holder{display:none!important}.video-js.vjs-ad-playing .vjs-control-bar .vjs-progress-control .vjs-progress-holder{cursor:auto;box-shadow:none;font-size:100%;margin:0 1em}.video-js .vjs-control-bar .ima-countdown-div{display:none;z-index:1}.video-js.vjs-ad-playing .vjs-control-bar .ima-countdown-div{display:block;position:absolute;margin-top:-2em;left:2em;font-size:1.2em}.video-js.vjs-disable-fullecreen .ima-fullscreen-div,.video-js.vjs-disable-fullecreen .vjs-fullscreen-control{display:none!important}"; (_dereq_("browserify-css").createStyle(css, { "href": "src/css/videojs-ima.css" }, { "insertAt": "bottom" })); module.exports = css;
},{"browserify-css":171}],405:[function(_dereq_,module,exports){
var css = ".text-shadow{text-shadow:0 0 2px rgba(0,0,0,.5)}.vjs-ios-skin .vjs-settings-button,.vjs5-hola-skin .vjs-settings-button{order:19}.vjs-ios-skin .vjs-settings-button>.vjs-button-icon,.vjs5-hola-skin .vjs-settings-button>.vjs-button-icon{height:100%;width:3.6em;padding:0;opacity:.9;transition:opacity 200ms ease}.vjs-ios-skin .vjs-settings-button>.vjs-button-icon>svg,.vjs5-hola-skin .vjs-settings-button>.vjs-button-icon>svg{fill:#fff;transform-origin:50% 50%;transition:transform 100ms ease}.vjs-ios-skin .vjs-settings-button[aria-expanded=true]>.vjs-button-icon>svg,.vjs5-hola-skin .vjs-settings-button[aria-expanded=true]>.vjs-button-icon>svg{transform:rotateZ(30deg)}.vjs-ios-skin .vjs-settings-button:hover>.vjs-button-icon,.vjs5-hola-skin .vjs-settings-button:hover>.vjs-button-icon{opacity:1}.vjs-ios-skin .vjs-settings-button.vjs-quality-4k .vjs-button-icon:after,.vjs-ios-skin .vjs-settings-button.vjs-quality-hd .vjs-button-icon:after,.vjs5-hola-skin .vjs-settings-button.vjs-quality-4k .vjs-button-icon:after,.vjs5-hola-skin .vjs-settings-button.vjs-quality-hd .vjs-button-icon:after{content:'';position:absolute;background-color:#f10;width:1.3em;height:.9em;left:1.8em;bottom:1.8em;background-size:contain;background-repeat:no-repeat}.vjs-ios-skin .vjs-settings-button.vjs-quality-hd .vjs-button-icon:after,.vjs5-hola-skin .vjs-settings-button.vjs-quality-hd .vjs-button-icon:after{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iOXB4IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxMyA5IiB3aWR0aD0iMTNweCIgZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgIDxwYXRoIGQ9Ik01LDcgTDYsNyBMNiw4IEw1LDggTDUsNyBaIE0xMCwzIEwxMCw0IEw4LDQgTDgsMyBMMTAsMyBaIE0zLDYgTDMsNSBMNSw1IEw1LDYgTDMsNiBaIE0yLDcgTDMsNyBMMyw4IEwyLDggTDIsNyBaIE03LDcgTDEwLDcgTDEwLDggTDcsOCBMNyw3IFogTTEwLDYgTDExLDYgTDExLDcgTDEwLDcgTDEwLDYgWiIgZmlsbD0iIzAwMCIgZmlsbC1vcGFjaXR5PSIwLjY0NzEiIGZpbGwtcnVsZT0iZXZlbm9kZCIgLz48cGF0aCBkPSJNNSw3IEw1LDYgTDUsNSBMMyw1IEwzLDYgTDMsNyBMMiw3IEwyLDIgTDMsMiBMMyw0IEw1LDQgTDUsMiBMNiwyIEw2LDcgTDUsNyBaIE0xMSw2IEwxMCw2IEwxMCw3IEw3LDcgTDcsMiBMMTAsMiBMMTAsMyBMMTEsMyBMMTEsNiBaIE0xMCw0IEwxMCwzIEw4LDMgTDgsNCBMOCw2IEwxMCw2IEwxMCw0IFoiLz4KPC9zdmc+)}.vjs-ios-skin .vjs-settings-button.vjs-quality-4k .vjs-button-icon:after,.vjs5-hola-skin .vjs-settings-button.vjs-quality-4k .vjs-button-icon:after{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iOXB4IiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3g9IjAgMCAxMyA5IiB3aWR0aD0iMTNweCIgZmlsbD0iI2ZmZiIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgIDxwYXRoIGQ9Ik0xMCw0IEwxMSw0IEwxMSw1IEwxMCw1IEwxMCw0IFogTTEwLDcgTDExLDcgTDExLDggTDEwLDggTDEwLDcgWiBNOCw1IEwxMCw1IEwxMCw2IEw4LDYgTDgsNSBaIE03LDcgTDgsNyBMOCw4IEw3LDggTDcsNyBaIE01LDYgTDYsNiBMNiw3IEw1LDcgTDUsNiBaIE00LDcgTDUsNyBMNSw4IEw0LDggTDQsNyBaIE0yLDYgTDQsNiBMNCw3IEwyLDcgTDIsNiBaIE0zLDQgTDQsNCBMNCw1IEwzLDUgTDMsNCBaIiBmaWxsPSIjMDAwIiBmaWxsLW9wYWNpdHk9IjAuNjQ3MSIgZmlsbC1ydWxlPSJldmVub2RkIiAvPjxwYXRoIGQ9Ik0xMSw1IEwxMSw3IEwxMCw3IEwxMCw2IEwxMCw1IEwxMSw1IFogTTEwLDUgTDgsNSBMOCw2IEw4LDcgTDcsNyBMNywyIEw4LDIgTDgsNCBMMTAsNCBMMTAsNSBaIE00LDQgTDMsNCBMMyw1IEw0LDUgTDQsNCBaIE00LDcgTDQsNiBMMiw2IEwyLDQgTDMsNCBMMywzIEw0LDMgTDQsMiBMNSwyIEw1LDUgTDYsNSBMNiw2IEw1LDYgTDUsNyBMNCw3IFogTTEwLDIgTDExLDIgTDExLDQgTDEwLDQgTDEwLDIgWiIvPgo8L3N2Zz4=)}.vjs-ios-skin .vjs-menu.vjs-rightclick-popup,.vjs-ios-skin .vjs-menu.vjs-settings-menu,.vjs5-hola-skin .vjs-menu.vjs-rightclick-popup,.vjs5-hola-skin .vjs-menu.vjs-settings-menu{background-color:#1c1c1c;background-color:rgba(28,28,28,.9);white-space:nowrap}.vjs-ios-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item,.vjs5-hola-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item{color:#eee;font-size:1em;line-height:3.3em;height:3.3em;cursor:pointer;padding:0 3.6em 0 1.5em;clear:both}.vjs-ios-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item[role=menuitemcheckbox],.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item[role=menuitemcheckbox],.vjs5-hola-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item[role=menuitemcheckbox],.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item[role=menuitemcheckbox]{padding:0 2.1em 0 3.6em}.vjs-ios-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item.vjs-selected:focus,.vjs-ios-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item.vjs-selected:hover,.vjs-ios-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item:focus,.vjs-ios-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item:hover,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-selected:focus,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-selected:hover,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item:focus,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item:hover,.vjs5-hola-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item.vjs-selected:focus,.vjs5-hola-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item.vjs-selected:hover,.vjs5-hola-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item:focus,.vjs5-hola-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item:hover,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-selected:focus,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-selected:hover,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item:focus,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item:hover{color:#eee;background-color:#fff;background-color:rgba(255,255,255,.1)}.vjs-ios-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item .vjs-menu-item-label,.vjs-ios-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item .vjs-menu-link,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item .vjs-menu-item-label,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item .vjs-menu-link,.vjs5-hola-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item .vjs-menu-item-label,.vjs5-hola-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item .vjs-menu-link,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item .vjs-menu-item-label,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item .vjs-menu-link{font-size:1.3em;font-weight:500}.vjs-ios-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item .vjs-menu-item-content,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item .vjs-menu-item-content,.vjs5-hola-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item .vjs-menu-item-content,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item .vjs-menu-item-content{font-size:1.2em;float:right;margin-left:3em}.vjs-ios-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item .vjs-menu-item-content .vjs-minor-label,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item .vjs-menu-item-content .vjs-minor-label,.vjs5-hola-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item .vjs-menu-item-content .vjs-minor-label,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item .vjs-menu-item-content .vjs-minor-label{padding-left:.2em;color:#ccc;font-size:.92em}.vjs-ios-skin .vjs-menu.vjs-settings-menu,.vjs5-hola-skin .vjs-menu.vjs-settings-menu{position:absolute;width:auto;right:.9em;bottom:4.8em;z-index:4;visibility:hidden;overflow:hidden;opacity:0;transition:opacity 100ms;will-change:width,height}.vjs-ios-skin .vjs-menu.vjs-settings-menu.vjs-size-transition,.vjs5-hola-skin .vjs-menu.vjs-settings-menu.vjs-size-transition{transition:height 200ms,width 200ms}.vjs-ios-skin .vjs-menu.vjs-settings-menu.vjs-lock-showing,.vjs5-hola-skin .vjs-menu.vjs-settings-menu.vjs-lock-showing{visibility:visible;opacity:1}.vjs-ios-skin .vjs-menu.vjs-settings-menu>.vjs-menu-content,.vjs5-hola-skin .vjs-menu.vjs-settings-menu>.vjs-menu-content{position:relative;width:auto;min-width:9em;padding:.5em 0;transition:background 200ms ease;cursor:default;overflow:hidden}.vjs-ios-skin .vjs-menu.vjs-settings-menu ::-webkit-scrollbar,.vjs5-hola-skin .vjs-menu.vjs-settings-menu ::-webkit-scrollbar{width:1em;background-color:#4c4c4c}.vjs-ios-skin .vjs-menu.vjs-settings-menu ::-webkit-scrollbar-track,.vjs5-hola-skin .vjs-menu.vjs-settings-menu ::-webkit-scrollbar-track{background-color:#4c4c4c}.vjs-ios-skin .vjs-menu.vjs-settings-menu ::-webkit-scrollbar-thumb,.vjs5-hola-skin .vjs-menu.vjs-settings-menu ::-webkit-scrollbar-thumb{background-color:#8d8d8d;border:1px solid #4c4c4c;border-radius:.5em}.vjs-ios-skin .vjs-menu.vjs-settings-menu>.vjs-menu-content:not(.vjs-active-submenu),.vjs5-hola-skin .vjs-menu.vjs-settings-menu>.vjs-menu-content:not(.vjs-active-submenu){position:fixed;top:0;left:0;visibility:hidden}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-submenu,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-submenu{position:relative;margin:4em 0 0;padding:0;border-top:1px solid rgba(255,255,255,.3);overflow:auto}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu{min-width:18em}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-4k,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-hd,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-4k,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-hd{padding-right:5.4em}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu>.vjs-menu-submenu,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu>.vjs-menu-submenu{margin-top:0;border-top:0}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-captions-options-submenu,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-captions-options-submenu{min-width:28em}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-menu-item-next,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-menu-item-next{text-align:left;background:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDMyIDMyIiB3aWR0aD0iMjQiIGZpbGw9IiNGRkZGRkYiPjxwYXRoIGQ9Im0gMTIuNTksMjAuMzQgNC41OCwtNC41OSAtNC41OCwtNC41OSAxLjQxLC0xLjQxIDYsNiAtNiw2IHoiLz48L3N2Zz4=) right .9em center/3.2em no-repeat}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-menu-item-next:focus,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-menu-item-next:hover,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-menu-item-next:focus,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-menu-item-next:hover{background-color:#fff;background-color:rgba(255,255,255,.1)}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-4k .vjs-menu-item-content,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-hd .vjs-menu-item-content,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item.vjs-quality-4k,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item.vjs-quality-hd,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-4k .vjs-menu-item-content,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-hd .vjs-menu-item-content,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item.vjs-quality-4k,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item.vjs-quality-hd{position:relative}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-4k .vjs-menu-item-content:after,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-hd .vjs-menu-item-content:after,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item.vjs-quality-4k:after,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item.vjs-quality-hd:after,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-4k .vjs-menu-item-content:after,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-hd .vjs-menu-item-content:after,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item.vjs-quality-4k:after,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item.vjs-quality-hd:after{position:absolute;font-size:.9em;color:#f10;vertical-align:super;margin-left:.2em;margin-top:-.5em}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-4k .vjs-menu-item-content:after,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-hd .vjs-menu-item-content:after,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-4k .vjs-menu-item-content:after,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-hd .vjs-menu-item-content:after{font-size:.75em;margin-left:.16em;margin-top:-.41em}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-hd .vjs-menu-item-content:after,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item.vjs-quality-hd:after,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-hd .vjs-menu-item-content:after,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item.vjs-quality-hd:after{content:'HD'}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-4k .vjs-menu-item-content:after,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item.vjs-quality-4k:after,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-main-submenu .vjs-menu-item.vjs-quality-4k .vjs-menu-item-content:after,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item.vjs-quality-4k:after{content:'4K'}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-quality-submenu .vjs-menu-item{text-align:right}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-submenu-title,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-submenu-title{position:absolute;top:0;line-height:4.5em;height:4.5em;padding:0 2.1em 0 3.6em;margin-bottom:.2em;text-align:left;background:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgZmlsbD0iI0ZGRkZGRiIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDMyIDMyIiB3aWR0aD0iMjQiPjxwYXRoIGQ9Ik0gMTkuNDEsMjAuMDkgMTQuODMsMTUuNSAxOS40MSwxMC45MSAxOCw5LjUgbCAtNiw2IDYsNiB6IiAvPjwvc3ZnPg==) left .3em center/3.2em no-repeat}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-submenu-title .vjs-minor-label,.vjs5-hola-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-submenu-title .vjs-minor-label{font-size:1.3em;margin-left:1.3em;text-decoration:underline;display:inline-block;cursor:pointer}.vjs-ios-skin.vjs-settings-expanded .vjs-progress-control:hover .vjs-mouse-display-tooltip,.vjs-ios-skin.vjs-settings-expanded .vjs-thumbnail,.vjs-ios-skin.vjs-settings-expanded .vjs-thumbnail-holder,.vjs-ios-skin.vjs-settings-expanded .vjs-tooltip,.vjs5-hola-skin.vjs-settings-expanded .vjs-progress-control:hover .vjs-mouse-display-tooltip,.vjs5-hola-skin.vjs-settings-expanded .vjs-thumbnail,.vjs5-hola-skin.vjs-settings-expanded .vjs-thumbnail-holder,.vjs5-hola-skin.vjs-settings-expanded .vjs-tooltip{display:none!important}.vjs-ios-skin.vjs-live:not(.vjs-dvr) .vjs-settings-menu,.vjs5-hola-skin.vjs-live:not(.vjs-dvr) .vjs-settings-menu{bottom:3.9em}.vjs-ios-skin .vjs-info-overlay,.vjs5-hola-skin .vjs-info-overlay{-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;background-color:#1c1c1c;background-color:rgba(28,28,28,.9);-webkit-box-shadow:.2em .2em .3em rgba(0,0,0,.5);-moz-box-shadow:.2em .2em .3em rgba(0,0,0,.5);box-shadow:.2em .2em .3em rgba(0,0,0,.5);position:absolute;top:1em;left:1em;z-index:3;display:inline-block;padding:1em;font-size:1.2em}.vjs-ios-skin .vjs-info-overlay.vjs-notify-overlay,.vjs5-hola-skin .vjs-info-overlay.vjs-notify-overlay{bottom:50%;top:auto;z-index:4;margin-bottom:-3em;left:50%;margin-left:-8.5em;opacity:1}.vjs-ios-skin .vjs-info-overlay.vjs-notify-overlay.vjs-notify-flash,.vjs5-hola-skin .vjs-info-overlay.vjs-notify-overlay.vjs-notify-flash{transition:opacity 500ms ease;transition-delay:2500ms;opacity:0}.vjs-ios-skin .vjs-info-overlay-title,.vjs5-hola-skin .vjs-info-overlay-title{margin-bottom:1em;font-size:1.2em}.vjs-ios-skin .vjs-info-overlay-x,.vjs5-hola-skin .vjs-info-overlay-x{position:absolute;top:1em;right:1em;transition:color 200ms ease;cursor:pointer}.vjs-ios-skin .vjs-info-overlay-x:hover,.vjs5-hola-skin .vjs-info-overlay-x:hover{color:#fff}.vjs-ios-skin .vjs-info-overlay-x:after,.vjs5-hola-skin .vjs-info-overlay-x:after{content:'\\00274c'}.vjs-ios-skin .vjs-notify-overlay-title,.vjs5-hola-skin .vjs-notify-overlay-title{margin-bottom:1em;font-size:1.5em}.vjs-ios-skin .vjs-notify-overlay-content,.vjs5-hola-skin .vjs-notify-overlay-content{text-align:center}.vjs-ios-skin .vjs-info-overlay-list,.vjs5-hola-skin .vjs-info-overlay-list{list-style:none;position:relative;margin:0;padding:0}.vjs-ios-skin .vjs-info-overlay-list-item,.vjs5-hola-skin .vjs-info-overlay-list-item{padding:0;margin:0;height:1.6em;line-height:1.6em}.vjs-ios-skin .vjs-info-overlay-list-item strong,.vjs5-hola-skin .vjs-info-overlay-list-item strong{font-weight:500;color:#fff}.vjs-ios-skin .vjs-info-overlay-list-item span,.vjs5-hola-skin .vjs-info-overlay-list-item span{color:#ccc}.vjs-ios-skin .vjs-rightclick-popup,.vjs5-hola-skin .vjs-rightclick-popup{position:absolute;z-index:6;width:auto;padding:.5em 0}.vjs-ios-skin .vjs-rightclick-popup .vjs-menu-item.vjs-powered-by,.vjs-ios-skin .vjs-rightclick-popup .vjs-menu-item.vjs-powered-by:hover,.vjs5-hola-skin .vjs-rightclick-popup .vjs-menu-item.vjs-powered-by,.vjs5-hola-skin .vjs-rightclick-popup .vjs-menu-item.vjs-powered-by:hover{line-height:4.5em;height:4.5em;padding:0 1em 0 4.3em;border-bottom:1px solid rgba(255,255,255,.3);background:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNzAgNzAuMDQiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDp1cmwoI3JhZGlhbC1ncmFkaWVudCk7fS5jbHMtMiwuY2xzLTQsLmNscy02e2ZpbGw6I2ViM2EyODt9LmNscy0ye29wYWNpdHk6MC4xO30uY2xzLTIsLmNscy02LC5jbHMtN3tpc29sYXRpb246aXNvbGF0ZTt9LmNscy0zLC5jbHMtNntvcGFjaXR5OjAuMTU7fS5jbHMtNXtmaWxsOiNmZmM4MDA7fS5jbHMtN3tmaWxsOiNmZmY7b3BhY2l0eTowLjI7fS5jbHMtOHtmaWxsOiNmZmI5MDA7fX08L3N0eWxlPjxyYWRpYWxHcmFkaWVudCBpZD0icmFkaWFsLWdyYWRpZW50IiBjeD0iMzUuNyIgY3k9IjQ3Ni40MyIgcj0iMzEuMDgiIGdyYWRpZW50VHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTAuODEgLTQzNy4xNikgc2NhbGUoMC45OSAxKSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2ZmZGMwMCIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI2ZhMCIvPjwvcmFkaWFsR3JhZGllbnQ+PC9kZWZzPjx0aXRsZT5zcGFya19sb2dvX2JsYWNrPC90aXRsZT48cGF0aCBjbGFzcz0iY2xzLTEiIGQ9Ik0zNSwwYy01LjgsMjAuNS0yNiwyNi4zLTI2LDQzLjlhMjYsMjYsMCwxLDAsNTIsMGgwQzYxLDI2LjM0LDQwLjgsMjAuNTQsMzUsMFoiLz48cGF0aCBjbGFzcz0iY2xzLTIiIGQ9Ik02MSw0My43NEM2MSwyNi4yNCw0MC44LDIwLjQ0LDM1LDBWNzBhMjYuMTQsMjYuMTQsMCwwLDAsMjYtMjYuM1oiLz48cGF0aCBkPSJNNDQuNSw0NGMxLjksMCwzLjUtMi4yLDMuNS01cy0xLjYtNS0zLjUtNVM0MSwzNi4yNCw0MSwzOSw0Mi42LDQ0LDQ0LjUsNDRaIi8+PHBhdGggZD0iTTI1LjUsNDRjMiwwLDMuNS0yLjIsMy41LTVzLTEuNi01LTMuNS01UzIyLDM2LjI0LDIyLDM5LDIzLjcsNDQsMjUuNSw0NFoiLz48ZyBjbGFzcz0iY2xzLTMiPjxlbGxpcHNlIGNsYXNzPSJjbHMtNCIgY3g9IjQ0LjUiIGN5PSI0MC4wNCIgcng9IjMuNSIgcnk9IjUiLz48ZWxsaXBzZSBjbGFzcz0iY2xzLTQiIGN4PSIyNS41IiBjeT0iNDAuMDQiIHJ4PSIzLjUiIHJ5PSI1Ii8+PHBhdGggY2xhc3M9ImNscy00IiBkPSJNNTIsNDVjLTEsMTEtNy45LDE2LjgtMTcsMTctOS0uMi0xNi02LTE3LTE3SDE1YTIwLDIwLDAsMSwwLDQwLDBaIi8+PC9nPjxwYXRoIGQ9Ik01Miw0NGMtMSwxMS03LjksMTYuOC0xNywxNy05LS4yLTE2LTYtMTctMTdIMTVhMjAsMjAsMCwxLDAsNDAsMFoiLz48cmVjdCBjbGFzcz0iY2xzLTUiIHg9IjQ1LjQ3IiB5PSIxLjQ3IiB3aWR0aD0iNy4xIiBoZWlnaHQ9IjcuMSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTAuODEgMzYuMTMpIHJvdGF0ZSgtNDUpIi8+PHJlY3QgY2xhc3M9ImNscy01IiB4PSIxLjMiIHk9IjI2LjM0IiB3aWR0aD0iNi40IiBoZWlnaHQ9IjYuNCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE5LjU3IDExLjgzKSByb3RhdGUoLTQ1KSIvPjxwYXRoIGNsYXNzPSJjbHMtNiIgZD0iTTM1LDY5QzIwLjgsNjksOS40LDU3LjM0LDksNDN2LjhhMjYsMjYsMCwxLDAsNTIsMGgwVjQzQzYwLjYsNTcuMzQsNDkuMiw2OSwzNSw2OVoiLz48cGF0aCBjbGFzcz0iY2xzLTciIGQ9Ik0zNSw0LjQ0YzUuOCwxNiwyNS4zLDIzLjEsMjYsNDB2LS42YzAtMTcuNS0yMC4yLTIzLjMtMjYtNDMuNy01LjgsMjAuMy0yNiwyNi4yLTI2LDQzLjd2LjZDOS43LDI3LjU0LDI5LjIsMjAuNDQsMzUsNC40NFoiLz48cGF0aCBjbGFzcz0iY2xzLTUiIGQ9Ik02MCwyNlY2QTEyLjg1LDEyLjg1LDAsMCwxLDUwLDE2LDEyLjg1LDEyLjg1LDAsMCwxLDYwLDI2WiIvPjxwYXRoIGNsYXNzPSJjbHMtOCIgZD0iTTYwLDZWMjZBMTMsMTMsMCwwLDEsNzAsMTYsMTMuMTIsMTMuMTIsMCwwLDEsNjAsNloiLz48cGF0aCBjbGFzcz0iY2xzLTUiIGQ9Ik0xMi41LDI1VjBBMTYuMDYsMTYuMDYsMCwwLDEsMCwxMi41NCwxNi4wNiwxNi4wNiwwLDAsMSwxMi41LDI1WiIvPjxwYXRoIGNsYXNzPSJjbHMtOCIgZD0iTTEyLjUsMFYyNUExNi4zMSwxNi4zMSwwLDAsMSwyNSwxMi40NCwxNS45NCwxNS45NCwwLDAsMSwxMi41LDBaIi8+PHBhdGggY2xhc3M9ImNscy0yIiBkPSJNNTAsMTZjNSwwLDksMywxMCw2LDEtMyw1LTYsMTAtNkExMy4xMiwxMy4xMiwwLDAsMCw2MCwyNiwxMi44NSwxMi44NSwwLDAsMCw1MCwxNloiLz48cGF0aCBjbGFzcz0iY2xzLTciIGQ9Ik0yNSwxMi41NEMxOSwxMiwxNCw5LDEyLjUsNSwxMSw5LDYsMTIsMCwxMi41NEExNi4zMywxNi4zMywwLDAsMCwxMi41LDAsMTYuMDYsMTYuMDYsMCwwLDAsMjUsMTIuNTRaIi8+PC9zdmc+) left 1.2em center/2.6em no-repeat;cursor:default}.vjs-ios-skin.vjs-ima-ad .vjs-rightclick-popup,.vjs5-hola-skin.vjs-ima-ad .vjs-rightclick-popup{z-index:1113}.vjs-ios-skin .vjs-menu .vjs-menu-item.vjs-selected,.vjs5-hola-skin .vjs-menu .vjs-menu-item.vjs-selected{background:url(data:image/svg+xml;base64,PHN2ZyBmaWxsPSIjRkZGRkZGIiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiIHdpZHRoPSIyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4gICAgPHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPiAgICA8cGF0aCBkPSJNOSAxNi4yTDQuOCAxMmwtMS40IDEuNEw5IDE5IDIxIDdsLTEuNC0xLjRMOSAxNi4yeiIvPjwvc3ZnPg==) .8em center/1.5em no-repeat}.vjs-ios-skin.vjs-fullscreen:not(.vjs-small) .vjs-info-overlay,.vjs-ios-skin.vjs-fullscreen:not(.vjs-small) .vjs-rightclick-popup,.vjs-ios-skin.vjs-fullscreen:not(.vjs-small) .vjs-settings-menu,.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small) .vjs-info-overlay,.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small) .vjs-rightclick-popup,.vjs5-hola-skin.vjs-fullscreen:not(.vjs-small) .vjs-settings-menu{font-size:150%}.vjs-ios-skin .vjs-control-bar .vjs-control.vjs-captions-button,.vjs-ios-skin .vjs-control-bar .vjs-control.vjs-subtitles-button,.vjs5-hola-skin .vjs-control-bar .vjs-control.vjs-captions-button,.vjs5-hola-skin .vjs-control-bar .vjs-control.vjs-subtitles-button{display:none}.vjs-ios-skin .vjs-captions-toggle,.vjs5-hola-skin .vjs-captions-toggle{width:3.6em;margin-right:.4em;order:9;opacity:.9;font-family:VideoJS;cursor:pointer}.vjs-ios-skin .vjs-captions-toggle>.vjs-button-icon,.vjs5-hola-skin .vjs-captions-toggle>.vjs-button-icon{height:100%;width:100%}.vjs-ios-skin .vjs-captions-toggle>.vjs-button-icon>svg,.vjs5-hola-skin .vjs-captions-toggle>.vjs-button-icon>svg{fill:#fff}.vjs-ios-skin .vjs-captions-toggle>.vjs-button-icon:after,.vjs5-hola-skin .vjs-captions-toggle>.vjs-button-icon:after{content:'';position:absolute;bottom:.6em;left:0;right:0;height:.2em;width:0;margin:0 auto;border-radius:.2em;background-color:#00b7f1;transition:width 250ms ease-in-out}.vjs-ios-skin .vjs-captions-toggle.vjs-pressed,.vjs-ios-skin .vjs-captions-toggle:hover,.vjs5-hola-skin .vjs-captions-toggle.vjs-pressed,.vjs5-hola-skin .vjs-captions-toggle:hover{opacity:1}.vjs-ios-skin .vjs-captions-toggle.vjs-pressed>.vjs-button-icon:after,.vjs5-hola-skin .vjs-captions-toggle.vjs-pressed>.vjs-button-icon:after{width:2em}.vjs-ios-skin .vjs-text-track-display>div>div>div,.vjs5-hola-skin .vjs-text-track-display>div>div>div{padding:1px .3em;border-radius:.1em;-webkit-box-decoration-break:clone;box-decoration-break:clone}.vjs-ios-skin .vjs-text-track-display svg,.vjs5-hola-skin .vjs-text-track-display svg{fill:#fff;height:1em;width:1em;vertical-align:bottom}.vjs-ios-skin.vjs-fullscreen:not(.vjs-small) .vjs-info-overlay,.vjs-ios-skin.vjs-fullscreen:not(.vjs-small) .vjs-rightclick-popup,.vjs-ios-skin.vjs-fullscreen:not(.vjs-small) .vjs-settings-menu{font-size:100%}.vjs-ios-skin .vjs-settings-button{width:23px}.vjs-ios-skin .vjs-settings-button>.vjs-button-icon{width:100%;height:100%;padding:0}.vjs-ios-skin .vjs-settings-button>.vjs-button-icon>svg{width:100%;height:100%;transform:scale(1.2)}.vjs-ios-skin .vjs-settings-button>.vjs-button-icon>svg>path{fill:rgba(255,255,255,.75)}.vjs-ios-skin .vjs-settings-button:active>.vjs-button-icon,.vjs-ios-skin[aria-expanded=true]>.vjs-button-icon>svg{transform:rotateZ(30deg) scale(1.2) scale(0.88888889)}.vjs-ios-skin .vjs-settings-button>.vjs-menu{left:auto;right:-9px;display:block;visibility:hidden;opacity:0;transition:visibility .5s,opacity .5s}.vjs-ios-skin .vjs-lock-showing.vjs-menu,.vjs-ios-skin .vjs-lock-showing>.vjs-menu{visibility:visible;opacity:1;transition:visibility .1s,opacity .1s}.vjs-ios-skin .vjs-menu-item.vjs-submenu-title,.vjs-ios-skin .vjs-menu.vjs-rightclick-popup .vjs-menu-item,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item,.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-item.vjs-submenu-title{height:31px;line-height:31px;padding-top:0;padding-bottom:0}.vjs-ios-skin .vjs-menu.vjs-rightclick-popup,.vjs-ios-skin .vjs-menu.vjs-settings-menu{background:0 0}.vjs-ios-skin .vjs-rightclick-popup{padding:0}.vjs-ios-skin .vjs-rightclick-popup,.vjs-ios-skin .vjs-rightclick-popup:after,.vjs-ios-skin .vjs-rightclick-popup:before{background:0 0;box-shadow:none}.vjs-ios-skin .vjs-rightclick-popup .vjs-menu-item,.vjs-ios-skin .vjs-rightclick-popup .vjs-menu-item.vjs-powered-by,.vjs-ios-skin .vjs-rightclick-popup .vjs-menu-item.vjs-powered-by:hover,.vjs-ios-skin .vjs-rightclick-popup .vjs-menu-item:hover{background-color:transparent}.vjs-ios-skin .vjs-rightclick-popup .vjs-menu-item.vjs-powered-by,.vjs-ios-skin .vjs-rightclick-popup .vjs-menu-item.vjs-powered-by:hover{height:45px;line-height:45px}.vjs-ios-skin .vjs-menu.vjs-settings-menu{right:6px;bottom:40px}.vjs-ios-skin .vjs-menu.vjs-settings-menu .vjs-menu-submenu{margin-top:31px}.vjs-ios-skin .vjs-picker-wrapper{position:relative;height:100%;width:100%;display:flex;align-items:center}.vjs-ios-skin .vjs-picker>.vjs-picker-wrapper{margin-top:31px;border-top:1px solid rgba(255,255,255,.3);overflow:hidden}.vjs-ios-skin .vjs-picker>.vjs-picker-wrapper>.vjs-picker-container{position:relative;display:block;width:100%;height:31px;border:1px solid transparent;border-left:none;border-right:none;overflow:visible;-webkit-perspective:1100px;perspective:1100px;transition:border .2s}.vjs-ios-skin .vjs-picker>.vjs-picker-wrapper>.vjs-picker-container>.vjs-menu-submenu{position:relative;margin:0;top:0;left:0;height:100%;width:100%;border-top:0;overflow:visible;transform-style:preserve-3d;-webkit-transform-style:preserve-3d}.vjs-ios-skin .vjs-picker>.vjs-picker-wrapper>.vjs-picker-container>.vjs-menu-submenu>.vjs-menu-item{position:absolute;left:0;top:0;width:100%;opacity:.5;color:rgba(255,255,255,.55);-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-tap-highlight-color:transparent}.vjs-ios-skin .vjs-picker>.vjs-picker-wrapper>.vjs-picker-container>.vjs-menu-submenu>.vjs-menu-item.vjs-selected:focus,.vjs-ios-skin .vjs-picker>.vjs-picker-wrapper>.vjs-picker-container>.vjs-menu-submenu>.vjs-menu-item.vjs-selected:hover,.vjs-ios-skin .vjs-picker>.vjs-picker-wrapper>.vjs-picker-container>.vjs-menu-submenu>.vjs-menu-item:focus,.vjs-ios-skin .vjs-picker>.vjs-picker-wrapper>.vjs-picker-container>.vjs-menu-submenu>.vjs-menu-item:hover{background:0 0}.vjs-ios-skin .vjs-picker>.vjs-picker-wrapper>.vjs-picker-container>.vjs-menu-submenu>.vjs-menu-item[role=menuitemcheckbox]{padding:0 2.1em}.vjs-ios-skin .vjs-picker>.vjs-picker-wrapper>.vjs-picker-container>.vjs-menu-submenu>.vjs-menu-item.vjs-selected{background:0 0;opacity:1;color:rgba(255,255,255,.75)}.vjs-ios-skin .vjs-rotate-transition .vjs-menu-submenu{transition:transform .2s linear}.vjs-ios-skin .vjs-rotate-transition>.vjs-picker-wrapper>.vjs-picker-container{border-color:rgba(255,255,255,.3)}.vjs-ios-skin .vjs-info-overlay{left:60px;top:50px;background:0 0;box-shadow:none}.vjs-ios-skin .vjs-info-overlay>.vjs-info-overlay-x{position:absolute}.vjs-ios-skin .vjs-control>.vjs-menu>.vjs-menu-content,.vjs-ios-skin .vjs-menu.vjs-settings-menu>.vjs-menu-content,.vjs-ios-skin .vjs-menu.vjs-settings-menu>.vjs-menu-content:not(.vjs-active-submenu){padding:0;top:auto}.vjs-ios-skin .vjs-menu>.vjs-menu-content .vjs-menu-submenu::-webkit-scrollbar,.vjs-ios-skin .vjs-menu>.vjs-menu-content::-webkit-scrollbar{width:0;background-color:transparent}.vjs-ios-skin .vjs-menu>.vjs-menu-content .vjs-menu-submenu::-webkit-scrollbar-track,.vjs-ios-skin .vjs-menu>.vjs-menu-content::-webkit-scrollbar-track{background-color:transparent}.vjs-ios-skin .vjs-menu>.vjs-menu-content .vjs-menu-submenu::-webkit-scrollbar-thumb,.vjs-ios-skin .vjs-menu>.vjs-menu-content::-webkit-scrollbar-thumb{background-color:transparent;border:none;border-radius:0}"; (_dereq_("browserify-css").createStyle(css, { "href": "src/css/videojs-settings.css" }, { "insertAt": "bottom" })); module.exports = css;
},{"browserify-css":171}],406:[function(_dereq_,module,exports){
var css = ".video-js .vjs-thumbnail-holder{position:absolute;display:none;overflow:hidden;outline:#2a2a2b solid 2px}.video-js .vjs-thumbnail-holder .vjs-thumbnail{position:relative;left:0;bottom:1.3em;opacity:0;padding:0!important;border:0!important;max-width:none!important;max-height:none!important;transition:opacity .2s ease;-webkit-transition:opacity .2s ease;-moz-transition:opacity .2s ease;-mz-transition:opacity .2s ease}.vjs-progress-control.fake-active .vjs-thumbnail,.vjs-progress-control:active .vjs-thumbnail,.vjs-progress-control:hover .vjs-thumbnail{opacity:1}.vjs-progress-control:active .vjs-thumbnail:active,.vjs-progress-control:hover .vjs-thumbnail:hover{opacity:0}"; (_dereq_("browserify-css").createStyle(css, { "href": "src/css/videojs-thumbnails.css" }, { "insertAt": "bottom" })); module.exports = css;
},{"browserify-css":171}],407:[function(_dereq_,module,exports){
var css = ".video-js.vjs-watermark{display:block}.video-js .vjs-watermark-content{opacity:.99;position:absolute;padding:5px;-webkit-transition:visibility 1s,opacity 1s;-moz-transition:visibility 1s,opacity 1s;-ms-transition:visibility 1s,opacity 1s;-o-transition:visibility 1s,opacity 1s;transition:visibility 1s,opacity 1s}.video-js .vjs-watermark-top-right{right:0;top:0}.video-js .vjs-watermark-top-left{left:0;top:0}.video-js .vjs-watermark-bottom-right{right:0;bottom:30px}.video-js .vjs-watermark-bottom-left{left:0;bottom:30px}.video-js.vjs-user-inactive.vjs-playing .vjs-watermark-fade{opacity:0}"; (_dereq_("browserify-css").createStyle(css, { "href": "src/css/videojs-watermark.css" }, { "insertAt": "bottom" })); module.exports = css;
},{"browserify-css":171}],408:[function(_dereq_,module,exports){
var css = ".video-js .vjs-big-play-button:before,.video-js .vjs-control:before,.video-js .vjs-modal-dialog,.vjs-modal-dialog .vjs-modal-dialog-content{position:absolute;top:0;left:0;width:100%;height:100%}.video-js .vjs-big-play-button:before,.video-js .vjs-control:before{text-align:center}@font-face{font-family:VideoJS;src:url(node_modules/video.js/dist/font/VideoJS.eot?#iefix) format(\"eot\")}@font-face{font-family:VideoJS;src:url(data:application/font-woff;charset=utf-8;base64, ) format(\"woff\"),url(data:application/x-font-ttf;charset=utf-8;base64, ) format(\"truetype\");font-weight:400;font-style:normal}.video-js .vjs-big-play-button,.video-js .vjs-play-control,.vjs-icon-play{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-big-play-button:before,.video-js .vjs-play-control:before,.vjs-icon-play:before{content:\"\\f101\"}.vjs-icon-play-circle{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-play-circle:before{content:\"\\f102\"}.video-js .vjs-play-control.vjs-playing,.vjs-icon-pause{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-play-control.vjs-playing:before,.vjs-icon-pause:before{content:\"\\f103\"}.video-js .vjs-mute-control.vjs-vol-0,.video-js .vjs-volume-menu-button.vjs-vol-0,.vjs-icon-volume-mute{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control.vjs-vol-0:before,.video-js .vjs-volume-menu-button.vjs-vol-0:before,.vjs-icon-volume-mute:before{content:\"\\f104\"}.video-js .vjs-mute-control.vjs-vol-1,.video-js .vjs-volume-menu-button.vjs-vol-1,.vjs-icon-volume-low{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control.vjs-vol-1:before,.video-js .vjs-volume-menu-button.vjs-vol-1:before,.vjs-icon-volume-low:before{content:\"\\f105\"}.video-js .vjs-mute-control.vjs-vol-2,.video-js .vjs-volume-menu-button.vjs-vol-2,.vjs-icon-volume-mid{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control.vjs-vol-2:before,.video-js .vjs-volume-menu-button.vjs-vol-2:before,.vjs-icon-volume-mid:before{content:\"\\f106\"}.video-js .vjs-mute-control,.video-js .vjs-volume-menu-button,.vjs-icon-volume-high{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mute-control:before,.video-js .vjs-volume-menu-button:before,.vjs-icon-volume-high:before{content:\"\\f107\"}.video-js .vjs-fullscreen-control,.vjs-icon-fullscreen-enter{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-fullscreen-control:before,.vjs-icon-fullscreen-enter:before{content:\"\\f108\"}.video-js.vjs-fullscreen .vjs-fullscreen-control,.vjs-icon-fullscreen-exit{font-family:VideoJS;font-weight:400;font-style:normal}.video-js.vjs-fullscreen .vjs-fullscreen-control:before,.vjs-icon-fullscreen-exit:before{content:\"\\f109\"}.vjs-icon-square{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-square:before{content:\"\\f10a\"}.vjs-icon-spinner{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-spinner:before{content:\"\\f10b\"}.video-js .vjs-subtitles-button,.vjs-icon-subtitles{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-subtitles-button:before,.vjs-icon-subtitles:before{content:\"\\f10c\"}.video-js .vjs-captions-button,.vjs-icon-captions{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-captions-button:before,.vjs-icon-captions:before{content:\"\\f10d\"}.video-js .vjs-chapters-button,.vjs-icon-chapters{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-chapters-button:before,.vjs-icon-chapters:before{content:\"\\f10e\"}.vjs-icon-share{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-share:before{content:\"\\f10f\"}.vjs-icon-cog{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-cog:before{content:\"\\f110\"}.video-js .vjs-mouse-display,.video-js .vjs-play-progress,.video-js .vjs-volume-display,.video-js .vjs-volume-level,.vjs-icon-circle{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-mouse-display:before,.video-js .vjs-play-progress:before,.video-js .vjs-volume-display:before,.video-js .vjs-volume-level:before,.vjs-icon-circle:before{content:\"\\f111\"}.vjs-icon-circle-outline{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-circle-outline:before{content:\"\\f112\"}.vjs-icon-circle-inner-circle{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-circle-inner-circle:before{content:\"\\f113\"}.vjs-icon-hd{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-hd:before{content:\"\\f114\"}.video-js .vjs-control.vjs-close-button,.vjs-icon-cancel{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-control.vjs-close-button:before,.vjs-icon-cancel:before{content:\"\\f115\"}.vjs-icon-replay{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-replay:before{content:\"\\f116\"}.vjs-icon-facebook{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-facebook:before{content:\"\\f117\"}.vjs-icon-gplus{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-gplus:before{content:\"\\f118\"}.vjs-icon-linkedin{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-linkedin:before{content:\"\\f119\"}.vjs-icon-twitter{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-twitter:before{content:\"\\f11a\"}.vjs-icon-tumblr{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-tumblr:before{content:\"\\f11b\"}.vjs-icon-pinterest{font-family:VideoJS;font-weight:400;font-style:normal}.vjs-icon-pinterest:before{content:\"\\f11c\"}.video-js .vjs-descriptions-button,.vjs-icon-audio-description{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-descriptions-button:before,.vjs-icon-audio-description:before{content:\"\\f11d\"}.video-js .vjs-audio-button,.vjs-icon-audio{font-family:VideoJS;font-weight:400;font-style:normal}.video-js .vjs-audio-button:before,.vjs-icon-audio:before{content:\"\\f11e\"}.video-js{display:block;vertical-align:top;box-sizing:border-box;color:#fff;background-color:#000;position:relative;padding:0;font-size:10px;line-height:1;font-weight:400;font-style:normal;font-family:Arial,Helvetica,sans-serif;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.video-js:-moz-full-screen{position:absolute}.video-js:-webkit-full-screen{width:100%!important;height:100%!important}.video-js *,.video-js :after,.video-js :before{box-sizing:inherit}.video-js ul{font-family:inherit;font-size:inherit;line-height:inherit;list-style-position:outside;margin:0}.video-js.vjs-16-9,.video-js.vjs-4-3,.video-js.vjs-fluid{width:100%;max-width:100%;height:0}.video-js.vjs-16-9{padding-top:56.25%}.video-js.vjs-4-3{padding-top:75%}.video-js.vjs-fill{width:100%;height:100%}.video-js .vjs-tech{position:absolute;top:0;left:0;width:100%;height:100%}body.vjs-full-window{padding:0;margin:0;height:100%;overflow-y:auto}.vjs-fake-fullscreen-parent{transform:none!important;overflow:visible!important;z-index:2147483647!important}.vjs-full-window .video-js.vjs-fullscreen{position:fixed!important;overflow:hidden;z-index:1000;left:0!important;top:0!important;bottom:0!important;right:0!important}.video-js.vjs-fullscreen{width:100%!important;height:100%!important;padding-top:0!important}.video-js.vjs-fullscreen.vjs-user-inactive{cursor:none}.vjs-hidden{display:none!important}.vjs-disabled{opacity:.5;cursor:default}.video-js .vjs-offscreen{height:1px;left:-9999px;position:absolute;top:0;width:1px}.vjs-lock-showing{display:block!important;opacity:1;visibility:visible}.vjs-no-js{padding:20px;color:#fff;background-color:#000;font-size:18px;font-family:Arial,Helvetica,sans-serif;text-align:center;width:300px;height:150px;margin:0 auto}.vjs-no-js a,.vjs-no-js a:visited{color:#66A8CC}.video-js .vjs-big-play-button{font-size:3em;line-height:1.5em;height:1.5em;width:3em;display:block;position:absolute;top:10px;left:10px;padding:0;cursor:pointer;opacity:1;border:.06666em solid #fff;background-color:#2B333F;background-color:rgba(43,51,63,.7);-webkit-border-radius:.3em;-moz-border-radius:.3em;border-radius:.3em;-webkit-transition:all .4s;-moz-transition:all .4s;-o-transition:all .4s;transition:all .4s}.vjs-big-play-centered .vjs-big-play-button{top:50%;left:50%;margin-top:-.75em;margin-left:-1.5em}.video-js .vjs-big-play-button:focus,.video-js:hover .vjs-big-play-button{outline:0;border-color:#fff;background-color:#73859f;background-color:rgba(115,133,159,.5);-webkit-transition:all 0s;-moz-transition:all 0s;-o-transition:all 0s;transition:all 0s}.vjs-controls-disabled .vjs-big-play-button,.vjs-error .vjs-big-play-button,.vjs-has-started .vjs-big-play-button,.vjs-using-native-controls .vjs-big-play-button{display:none}.video-js button{background:0 0;border:none;color:inherit;display:inline-block;overflow:visible;font-size:inherit;line-height:inherit;text-transform:none;text-decoration:none;transition:none;-webkit-appearance:none;-moz-appearance:none;appearance:none}.video-js .vjs-control.vjs-close-button{cursor:pointer;height:3em;position:absolute;right:0;top:.5em;z-index:2}.video-js .vjs-tooltip{position:absolute;top:-3em;left:0;z-index:10;display:none;padding:6px 8px 8px;-webkit-border-radius:.3em;-moz-border-radius:.3em;border-radius:.3em;background-color:#2B333F;background-color:rgba(43,51,63,.7);color:#fff;font-size:.9em;font-family:Arial,Helvetica,sans-serif;white-space:nowrap;pointer-events:none}.video-js .vjs-tooltip-active{display:block}.vjs-menu-button{cursor:pointer}.vjs-menu-button.vjs-disabled{cursor:default}.vjs-workinghover .vjs-menu-button.vjs-disabled:hover .vjs-menu{display:none}.vjs-menu .vjs-menu-content{display:block;padding:0;margin:0;overflow:auto}.vjs-scrubbing .vjs-menu-button:hover .vjs-menu{display:none}.vjs-menu li{list-style:none;margin:0;padding:.2em 0;line-height:1.4em;font-size:1.2em;text-align:center;text-transform:lowercase}.vjs-menu li:focus,.vjs-menu li:hover{outline:0;background-color:#73859f;background-color:rgba(115,133,159,.5)}.vjs-menu li.vjs-selected,.vjs-menu li.vjs-selected:focus,.vjs-menu li.vjs-selected:hover{background-color:#fff;color:#2B333F}.vjs-menu li.vjs-menu-title{text-align:center;text-transform:uppercase;font-size:1em;line-height:2em;padding:0;margin:0 0 .3em;font-weight:700;cursor:default}.vjs-control .vjs-menu{text-shadow:none}.vjs-menu-button-popup .vjs-menu{display:none;position:absolute;bottom:0;width:10em;left:-3em;height:0;margin-bottom:1.5em;border-top-color:rgba(43,51,63,.7)}.vjs-menu-button-popup .vjs-menu .vjs-menu-content{background-color:#2B333F;background-color:rgba(43,51,63,.7);position:absolute;width:100%;bottom:1.5em;max-height:15em}.vjs-menu-button-popup .vjs-menu.vjs-lock-showing,.vjs-workinghover .vjs-menu-button-popup:hover .vjs-menu{display:block}.video-js .vjs-menu-button-inline{-webkit-transition:all .4s;-moz-transition:all .4s;-o-transition:all .4s;transition:all .4s;overflow:hidden}.video-js .vjs-menu-button-inline:before{width:2.222222222em}.video-js .vjs-menu-button-inline.vjs-slider-active:not(.vjs-no-volume),.video-js .vjs-menu-button-inline:focus:not(.vjs-no-volume),.video-js .vjs-menu-button-inline:hover:not(.vjs-no-volume),.video-js.vjs-no-flex .vjs-menu-button-inline:not(.vjs-no-volume){width:12em}.video-js .vjs-menu-button-inline.vjs-slider-active{-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.vjs-menu-button-inline .vjs-menu{opacity:0;height:100%;width:auto;position:absolute;left:4em;top:0;padding:0;margin:0;-webkit-transition:all .4s;-moz-transition:all .4s;-o-transition:all .4s;transition:all .4s}.vjs-menu-button-inline.vjs-slider-active .vjs-menu,.vjs-menu-button-inline:focus .vjs-menu,.vjs-menu-button-inline:hover .vjs-menu{display:block;opacity:1}.vjs-no-flex .vjs-menu-button-inline .vjs-menu{display:block;opacity:1;position:relative;width:auto}.vjs-no-flex .vjs-menu-button-inline.vjs-slider-active .vjs-menu,.vjs-no-flex .vjs-menu-button-inline:focus .vjs-menu,.vjs-no-flex .vjs-menu-button-inline:hover .vjs-menu{width:auto}.vjs-menu-button-inline .vjs-menu-content{width:auto;height:100%;margin:0;overflow:hidden}.video-js .vjs-control-bar{display:none;width:100%;position:absolute;bottom:0;left:0;right:0;height:3em;background-color:#2B333F;background-color:rgba(43,51,63,.7)}.vjs-has-started .vjs-control-bar{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;visibility:visible;opacity:1;-webkit-transition:visibility .1s,opacity .1s;-moz-transition:visibility .1s,opacity .1s;-o-transition:visibility .1s,opacity .1s;transition:visibility .1s,opacity .1s}.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar{visibility:visible;opacity:0;-webkit-transition:visibility 1s,opacity 1s;-moz-transition:visibility 1s,opacity 1s;-o-transition:visibility 1s,opacity 1s;transition:visibility 1s,opacity 1s}@media \\0screen{.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar{visibility:hidden}}.vjs-controls-disabled .vjs-control-bar,.vjs-error .vjs-control-bar,.vjs-using-native-controls .vjs-control-bar{display:none!important}.video-js:not(.vjs-using-native-controls) video::-webkit-media-controls-panel,.video-js:not(.vjs-using-native-controls) video::-webkit-media-controls-panel-container,.video-js:not(.vjs-using-native-controls) video::-webkit-media-controls-start-playback-button{display:none!important;-webkit-appearance:none}.vjs-audio.vjs-has-started.vjs-user-inactive.vjs-playing .vjs-control-bar{opacity:1;visibility:visible}@media \\0screen{.vjs-user-inactive.vjs-playing .vjs-control-bar :before{content:\"\"}}.vjs-has-started.vjs-no-flex .vjs-control-bar{display:table}.video-js .vjs-control{outline:0;position:relative;text-align:center;margin:0;padding:0;height:100%;width:4em;-webkit-box-flex:none;-moz-box-flex:none;-webkit-flex:none;-ms-flex:none;flex:none}.video-js .vjs-control:before{font-size:1.8em;line-height:1.67}.video-js .vjs-control:focus,.video-js .vjs-control:focus:before,.video-js .vjs-control:hover:before{text-shadow:0 0 1em #fff}.video-js .vjs-control-text{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.vjs-no-flex .vjs-control{display:table-cell;vertical-align:middle}.video-js .vjs-custom-control-spacer{display:none}.video-js .vjs-progress-control{-webkit-box-flex:auto;-moz-box-flex:auto;-webkit-flex:auto;-ms-flex:auto;flex:auto;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;min-width:4em}.vjs-live .vjs-progress-control{display:none}.video-js .vjs-progress-holder{-webkit-box-flex:auto;-moz-box-flex:auto;-webkit-flex:auto;-ms-flex:auto;flex:auto;-webkit-transition:all .2s;-moz-transition:all .2s;-o-transition:all .2s;transition:all .2s;height:.3em}.video-js .vjs-progress-control:hover .vjs-progress-holder{font-size:1.666666666666666666em}.video-js .vjs-progress-control:hover .vjs-mouse-display-tooltip,.video-js .vjs-progress-control:hover .vjs-play-progress:after,.video-js .vjs-progress-control:hover .vjs-time-tooltip{font-family:Arial,Helvetica,sans-serif;visibility:visible;font-size:.6em}.video-js .vjs-progress-holder .vjs-load-progress,.video-js .vjs-progress-holder .vjs-load-progress div,.video-js .vjs-progress-holder .vjs-play-progress,.video-js .vjs-progress-holder .vjs-tooltip-progress-bar{position:absolute;display:block;height:.3em;margin:0;padding:0;width:0;left:0;top:0}.video-js .vjs-mouse-display:before{display:none}.video-js .vjs-play-progress{background-color:#fff}.video-js .vjs-play-progress:before{position:absolute;top:-.333333333333333em;right:-.5em;font-size:.9em}.video-js .vjs-mouse-display-tooltip,.video-js .vjs-play-progress:after,.video-js .vjs-time-tooltip{visibility:hidden;pointer-events:none;position:absolute;top:-3.4em;right:-1.9em;font-size:.9em;color:#000;content:attr(data-current-time);padding:6px 8px 8px;background-color:#fff;background-color:rgba(255,255,255,.8);-webkit-border-radius:.3em;-moz-border-radius:.3em;border-radius:.3em}.video-js .vjs-play-progress:after,.video-js .vjs-play-progress:before,.video-js .vjs-time-tooltip{z-index:1}.video-js .vjs-progress-control .vjs-keep-tooltips-inside:after{display:none}.video-js .vjs-load-progress{background:#bfc7d3;background:rgba(115,133,159,.5)}.video-js .vjs-load-progress div{background:#fff;background:rgba(115,133,159,.75)}.video-js.vjs-no-flex .vjs-progress-control{width:auto}.video-js .vjs-time-tooltip{display:inline-block;height:2.4em;position:relative;float:right;right:-1.9em}.vjs-tooltip-progress-bar{visibility:hidden}.video-js .vjs-progress-control .vjs-mouse-display{display:none;position:absolute;width:1px;height:100%;background-color:#000;z-index:1}.vjs-no-flex .vjs-progress-control .vjs-mouse-display{z-index:0}.video-js .vjs-progress-control:hover .vjs-mouse-display{display:block}.video-js.vjs-user-inactive .vjs-progress-control .vjs-mouse-display,.video-js.vjs-user-inactive .vjs-progress-control .vjs-mouse-display-tooltip{visibility:hidden;opacity:0;-webkit-transition:visibility 1s,opacity 1s;-moz-transition:visibility 1s,opacity 1s;-o-transition:visibility 1s,opacity 1s;transition:visibility 1s,opacity 1s}.video-js.vjs-user-inactive.vjs-no-flex .vjs-progress-control .vjs-mouse-display,.video-js.vjs-user-inactive.vjs-no-flex .vjs-progress-control .vjs-mouse-display-tooltip{display:none}.video-js .vjs-progress-control .vjs-mouse-display-tooltip,.vjs-mouse-display .vjs-time-tooltip{color:#fff;background-color:#000;background-color:rgba(0,0,0,.8)}.video-js .vjs-slider{outline:0;position:relative;cursor:pointer;padding:0;margin:0 .45em;background-color:#73859f;background-color:rgba(115,133,159,.5)}.video-js .vjs-slider:focus{text-shadow:0 0 1em #fff;-webkit-box-shadow:0 0 1em #fff;-moz-box-shadow:0 0 1em #fff;box-shadow:0 0 1em #fff}.video-js .vjs-mute-control,.video-js .vjs-volume-menu-button{cursor:pointer;-webkit-box-flex:none;-moz-box-flex:none;-webkit-flex:none;-ms-flex:none;flex:none}.video-js .vjs-volume-control{width:5em;-webkit-box-flex:none;-moz-box-flex:none;-webkit-flex:none;-ms-flex:none;flex:none;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.video-js .vjs-volume-bar{margin:1.35em .45em}.vjs-volume-bar.vjs-slider-horizontal{width:5em;height:.3em}.vjs-volume-bar.vjs-slider-vertical{width:.3em;height:5em;margin:1.35em auto}.video-js .vjs-volume-level{position:absolute;bottom:0;left:0;background-color:#fff}.video-js .vjs-volume-level:before{position:absolute;font-size:.9em}.vjs-slider-vertical .vjs-volume-level{width:.3em}.vjs-slider-vertical .vjs-volume-level:before{top:-.5em;left:-.3em}.vjs-slider-horizontal .vjs-volume-level{height:.3em}.vjs-slider-horizontal .vjs-volume-level:before{top:-.3em;right:-.5em}.vjs-volume-bar.vjs-slider-vertical .vjs-volume-level{height:100%}.vjs-volume-bar.vjs-slider-horizontal .vjs-volume-level{width:100%}.vjs-menu-button-popup.vjs-volume-menu-button .vjs-menu{display:block;width:0;height:0;border-top-color:transparent}.vjs-menu-button-popup.vjs-volume-menu-button-vertical .vjs-menu{left:.5em;height:8em}.vjs-menu-button-popup.vjs-volume-menu-button-horizontal .vjs-menu{left:-2em}.vjs-menu-button-popup.vjs-volume-menu-button .vjs-menu-content{height:0;width:0;overflow-x:hidden;overflow-y:hidden}.vjs-volume-menu-button-vertical .vjs-lock-showing .vjs-menu-content,.vjs-volume-menu-button-vertical.vjs-slider-active .vjs-menu-content,.vjs-volume-menu-button-vertical:focus .vjs-menu-content,.vjs-volume-menu-button-vertical:hover .vjs-menu-content{height:8em;width:2.9em}.vjs-volume-menu-button-horizontal .vjs-lock-showing .vjs-menu-content,.vjs-volume-menu-button-horizontal .vjs-slider-active .vjs-menu-content,.vjs-volume-menu-button-horizontal:focus .vjs-menu-content,.vjs-volume-menu-button-horizontal:hover .vjs-menu-content{height:2.9em;width:8em}.vjs-volume-menu-button.vjs-menu-button-inline .vjs-menu-content{background-color:transparent!important}.video-js .vjs-volume-display:before{display:none}.video-js .vjs-volume-display-tooltip{display:none;position:absolute;top:-1em;font-size:1em;padding:.5em;-webkit-border-radius:.3em;-moz-border-radius:.3em;border-radius:.3em}.video-js .vjs-volume-display-tooltip-active{display:block}.video-js .vjs-volume-bar .vjs-volume-display{position:absolute;top:0;z-index:1;display:none;width:1px;height:100%;background-color:#000}.vjs-no-flex .vjs-volume-bar .vjs-volume-display{z-index:0}.video-js .vjs-volume-bar:hover .vjs-volume-display{display:block}.video-js.vjs-user-inactive .vjs-volume-bar .vjs-volume-display,.video-js.vjs-user-inactive .vjs-volume-display-tooltip{visibility:hidden;opacity:0;-webkit-transition:visibility 1s,opacity 1s;-moz-transition:visibility 1s,opacity 1s;-o-transition:visibility 1s,opacity 1s;transition:visibility 1s,opacity 1s}.video-js.vjs-user-inactive.vjs-no-flex .vjs-volume-bar .vjs-volume-display,.video-js.vjs-user-inactive.vjs-no-flex .vjs-volume-display-tooltip{display:none}.video-js .vjs-volume-display-tooltip{color:#fff;background-color:#000;background-color:rgba(0,0,0,.8)}.vjs-poster{display:inline-block;vertical-align:middle;background-repeat:no-repeat;background-position:50% 50%;background-size:contain;background-color:#000;cursor:pointer;margin:0;padding:0;position:absolute;top:0;right:0;bottom:0;left:0;height:100%}.vjs-poster img{display:block;vertical-align:middle;margin:0 auto;max-height:100%;padding:0;width:100%}.vjs-has-started .vjs-poster{display:none}.vjs-audio.vjs-has-started .vjs-poster{display:block}.vjs-controls-disabled .vjs-poster,.vjs-using-native-controls .vjs-poster{display:none}.video-js .vjs-live-control{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:flex-start;-webkit-align-items:flex-start;-ms-flex-align:flex-start;align-items:flex-start;-webkit-box-flex:auto;-moz-box-flex:auto;-webkit-flex:auto;-ms-flex:auto;flex:auto;font-size:1em;line-height:3em}.vjs-no-flex .vjs-live-control{display:table-cell;width:auto;text-align:left}.video-js .vjs-time-control{-webkit-box-flex:none;-moz-box-flex:none;-webkit-flex:none;-ms-flex:none;flex:none;font-size:1em;line-height:3em;min-width:2em;width:auto;padding-left:1em;padding-right:1em}.video-js .vjs-current-time,.video-js .vjs-duration,.vjs-live .vjs-time-control,.vjs-no-flex .vjs-current-time,.vjs-no-flex .vjs-duration{display:none}.vjs-time-divider{display:none;line-height:3em}.vjs-live .vjs-time-divider{display:none}.video-js .vjs-play-control{cursor:pointer;-webkit-box-flex:none;-moz-box-flex:none;-webkit-flex:none;-ms-flex:none;flex:none}.vjs-text-track-display{position:absolute;bottom:3em;left:0;right:0;top:0;pointer-events:none}.video-js.vjs-user-inactive.vjs-playing .vjs-text-track-display{bottom:1em}.video-js .vjs-text-track{font-size:1.4em;text-align:center;margin-bottom:.1em;background-color:#000;background-color:rgba(0,0,0,.5)}.vjs-subtitles{color:#fff}.vjs-captions{color:#fc6}.vjs-tt-cue{display:block}video::-webkit-media-text-track-display{-moz-transform:translateY(-3em);-ms-transform:translateY(-3em);-o-transform:translateY(-3em);-webkit-transform:translateY(-3em);transform:translateY(-3em)}.video-js.vjs-user-inactive.vjs-playing video::-webkit-media-text-track-display{-moz-transform:translateY(-1.5em);-ms-transform:translateY(-1.5em);-o-transform:translateY(-1.5em);-webkit-transform:translateY(-1.5em);transform:translateY(-1.5em)}.video-js .vjs-fullscreen-control{cursor:pointer;-webkit-box-flex:none;-moz-box-flex:none;-webkit-flex:none;-ms-flex:none;flex:none}.vjs-playback-rate .vjs-playback-rate-value{font-size:1.5em;line-height:2;position:absolute;top:0;left:0;width:100%;height:100%;text-align:center}.vjs-playback-rate .vjs-menu{width:4em;left:0}.vjs-error .vjs-error-display .vjs-modal-dialog-content{font-size:1.4em;text-align:center}.vjs-error .vjs-error-display:before{color:#fff;content:'X';font-family:Arial,Helvetica,sans-serif;font-size:4em;left:0;line-height:1;margin-top:-.5em;position:absolute;text-shadow:.05em .05em .1em #000;text-align:center;top:50%;vertical-align:middle;width:100%}.vjs-loading-spinner{display:none;position:absolute;top:50%;left:50%;margin:-25px 0 0 -25px;opacity:.85;text-align:left;border:6px solid rgba(43,51,63,.7);box-sizing:border-box;background-clip:padding-box;width:50px;height:50px;border-radius:25px}.vjs-seeking .vjs-loading-spinner,.vjs-waiting .vjs-loading-spinner{display:block}.vjs-using-native-controls .vjs-loading-spinner{display:none!important}.vjs-loading-spinner:after,.vjs-loading-spinner:before{content:\"\";position:absolute;margin:-6px;box-sizing:inherit;width:inherit;height:inherit;border-radius:inherit;opacity:1;border:inherit;border-color:#fff transparent transparent}.vjs-seeking .vjs-loading-spinner:after,.vjs-seeking .vjs-loading-spinner:before,.vjs-waiting .vjs-loading-spinner:after,.vjs-waiting .vjs-loading-spinner:before{-webkit-animation:vjs-spinner-spin 1.1s cubic-bezier(0.6,.2,0,.8) infinite,vjs-spinner-fade 1.1s linear infinite;animation:vjs-spinner-spin 1.1s cubic-bezier(0.6,.2,0,.8) infinite,vjs-spinner-fade 1.1s linear infinite}.vjs-seeking .vjs-loading-spinner:before,.vjs-waiting .vjs-loading-spinner:before{border-top-color:#fff}.vjs-seeking .vjs-loading-spinner:after,.vjs-waiting .vjs-loading-spinner:after{border-top-color:#fff;-webkit-animation-delay:.44s;animation-delay:.44s}@keyframes vjs-spinner-spin{100%{transform:rotate(360deg)}}@-webkit-keyframes vjs-spinner-spin{100%{-webkit-transform:rotate(360deg)}}@keyframes vjs-spinner-fade{0%,20%{border-top-color:#73859f}35%{border-top-color:#fff}100%,60%{border-top-color:#73859f}}@-webkit-keyframes vjs-spinner-fade{0%,20%{border-top-color:#73859f}35%{border-top-color:#fff}100%,60%{border-top-color:#73859f}}.vjs-chapters-button .vjs-menu ul{width:24em}.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-custom-control-spacer{-webkit-box-flex:auto;-moz-box-flex:auto;-webkit-flex:auto;-ms-flex:auto;flex:auto}.video-js.vjs-layout-tiny:not(.vjs-fullscreen).vjs-no-flex .vjs-custom-control-spacer{width:auto}.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-captions-button,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-chapters-button,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-current-time,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-duration,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-mute-control,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-playback-rate,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-remaining-time,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-subtitles-button,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-time-divider,.video-js.vjs-layout-small:not(.vjs-fullscreen) .vjs-volume-control,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-captions-button,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-chapters-button,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-current-time,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-duration,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-mute-control,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-playback-rate,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-progress-control,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-remaining-time,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-subtitles-button,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-time-divider,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-volume-control,.video-js.vjs-layout-tiny:not(.vjs-fullscreen) .vjs-volume-menu-button,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-captions-button,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-chapters-button,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-current-time,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-duration,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-mute-control,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-playback-rate,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-remaining-time,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-subtitles-button,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-time-divider,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-volume-control,.video-js.vjs-layout-x-small:not(.vjs-fullscreen) .vjs-volume-menu-button{display:none}.vjs-caption-settings{position:relative;top:1em;background-color:#2B333F;background-color:rgba(43,51,63,.75);color:#fff;margin:0 auto;padding:.5em;height:15em;font-size:12px;width:40em}.vjs-caption-settings .vjs-tracksettings{top:0;bottom:2em;left:0;right:0;position:absolute;overflow:auto}.vjs-caption-settings .vjs-tracksettings-colors,.vjs-caption-settings .vjs-tracksettings-font{float:left}.vjs-caption-settings .vjs-tracksettings-colors:after,.vjs-caption-settings .vjs-tracksettings-controls:after,.vjs-caption-settings .vjs-tracksettings-font:after{clear:both}.vjs-caption-settings .vjs-tracksettings-controls{position:absolute;bottom:1em;right:1em}.vjs-caption-settings .vjs-tracksetting{margin:5px;padding:3px;min-height:40px}.vjs-caption-settings .vjs-tracksetting label{display:block;width:100px;margin-bottom:5px}.vjs-caption-settings .vjs-tracksetting span{display:inline;margin-left:5px}.vjs-caption-settings .vjs-tracksetting>div{margin-bottom:5px;min-height:20px}.vjs-caption-settings .vjs-tracksetting>div:last-child{margin-bottom:0;padding-bottom:0;min-height:0}.vjs-caption-settings label>input{margin-right:10px}.vjs-caption-settings input[type=button]{width:40px;height:40px}.video-js .vjs-modal-dialog{background:rgba(0,0,0,.8);background:-webkit-linear-gradient(-90deg,rgba(0,0,0,.8),rgba(255,255,255,0));background:linear-gradient(180deg,rgba(0,0,0,.8),rgba(255,255,255,0))}.vjs-modal-dialog .vjs-modal-dialog-content{font-size:1.2em;line-height:1.5;padding:20px 24px;z-index:1}"; (_dereq_("browserify-css").createStyle(css, { "href": "src/css/videojs.css" }, { "insertAt": "bottom" })); module.exports = css;
},{"browserify-css":171}],409:[function(_dereq_,module,exports){
'use strict';
var vjs = _dereq_('video.js');
var throttle = _dereq_('lodash/throttle');
var get = _dereq_('lodash/get');

vjs.plugin('dvr', function(){
    var player = this;
    function remove_child(component, child_name){
        var child = component.getChild(child_name);
        component.removeChild(child);
        child.dispose();
    }
    function init(){
        var hls = player.tech_.hls_obj;
        var flashls = player.tech_.flashlsProvider;
        var native_hls = false;
        try {
            native_hls = player.currentType_.indexOf('application') === 0 &&
                player.tech_.el_.src == player.src();
        } catch(e){}
        var seekable = player.seekable();
        var is_hls = hls || flashls || native_hls;
        if (!is_hls || player.duration()!=Infinity || !seekable ||
            !seekable.length || (seekable.end(0)-seekable.start(0))<60)
        {
            player.trigger('dvrReady', false);
            return;
        }
        var progressControl = player.controlBar.progressControl;
        remove_child(progressControl, 'seekBar');
        progressControl.seekBar = progressControl.addChild('DvrSeekBar');
        remove_child(player.controlBar, 'liveDisplay');
        player.controlBar.addChild('LiveButton');
        player.addClass('vjs-dvr');
        if (!player.hasStarted())
            player.one('play', function(){ player.dvr.seek_to_live(); });
        player.on('timeupdate', function(){
            player.toggleClass('vjs-dvr-live', player.dvr.is_live()); });
        if (flashls)
            player.dvr.live_threshold = Math.max(flashls.avg_duration*1.5, 10);
        else
        {
            hls.on('hlsLevelUpdated', function(e, data){
                player.dvr.live_threshold =
                    Math.max(data.details.targetduration*1.5, 10);
            });
        }
        player.trigger('dvrReady', true);
    }
    player.ready(function(){
        var seekable = player.seekable();
        if (player.duration() && seekable && seekable.length)
            return void init();
        player.one('loadedmetadata', function(){
            player.on('timeupdate', function on_timeupdate(){
                var seekable = player.seekable();
                if (!seekable || !seekable.length)
                    return;
                player.off('timeupdate', on_timeupdate);
                init();
            });
        });
    });
    player.dvr = {
        live_threshold: 10,
        range: function(){
            var seekable = player.seekable();
            return seekable && seekable.length ?
                {start: seekable.start(0), end: seekable.end(0)} : null;
        },
        is_live: function(){
            var range = this.range();
            var end = range && range.end;
            return end && (end-player.currentTime()) <= this.live_threshold;
        },
        format_time: function(time){
            var range = this.range();
            if (!range)
                return '0:00';
            if (!time)
            {
                time = player.scrubbing() ? player.getCache().currentTime :
                    player.currentTime();
            }
            if (range.end-time < this.live_threshold)
                return player.localize('Live');
            time = Math.max(range.end-time, 0);
            return (time > 0 ? '-' : '')+vjs.formatTime(time, range.end);
        },
        seek_to_live: function(){
            var range = this.range();
            if (range && !this.is_live())
                player.currentTime(range.end);
        },
    };
});

var SeekBar = vjs.getComponent('SeekBar');
vjs.registerComponent('DvrSeekBar', vjs.extend(SeekBar, {
    options_: {
        children: ['dvrLoadProgressBar', 'dvrMouseTimeDisplay',
            'dvrPlayProgressBar'],
        barName: 'dvrPlayProgressBar'
    },
    getPercent: function(){
        var dvr = this.player_.dvr;
        var range = dvr.range();
        return (!range || dvr.is_live()) ? 1 :
            (this.player_.currentTime()-range.start) /
            (range.end-range.start);
    },
    handleMouseMove: function(event){
        var range = this.player_.dvr.range();
        if (!range)
            return;
        var time = range.start + this.calculateDistance(event) *
            (range.end-range.start);
        if (range.end-time < this.player_.dvr.live_threshold)
            this.player_.dvr.seek_to_live();
        else
            this.player_.currentTime(Math.min(time, range.end));
    },
    updateAriaAttributes: function(el){
        el.setAttribute('aria-valuenow', (this.getPercent() * 100).toFixed(2));
        el.setAttribute('aria-valuetext', this.player_.dvr.format_time());
    },
}));

var PlayProgressBar = vjs.getComponent('PlayProgressBar');
vjs.registerComponent('DvrPlayProgressBar', vjs.extend(PlayProgressBar, {
    updateDataAttr: function(){
        this.el_.setAttribute('data-current-time',
            this.player_.dvr.format_time());
    },
}));

function el_pos(el){
    var box;
    if (el.getBoundingClientRect && el.parentNode)
        box = el.getBoundingClientRect();
    else
        return 0;
    var body = document.body;
    var client = document.documentElement.clientLeft || body.clientLeft || 0;
    var scroll = window.pageXOffset || body.scrollLeft;
    return Math.round(box.left + scroll - client);
}

var MouseTimeDisplay = vjs.getComponent('MouseTimeDisplay');
var Component = vjs.getComponent('Component');
vjs.registerComponent('DvrMouseTimeDisplay', vjs.extend(MouseTimeDisplay, {
    constructor: function(player, options){
        Component.call(this, player, options);
        this.keepTooltipsInside = get(options,
            'playerOptions.controlBar.progressControl.keepTooltipsInside');
        if (this.keepTooltipsInside)
        {
            this.tooltip = vjs.createEl('div',
                {className: 'vjs-time-tooltip'});
            this.el().appendChild(this.tooltip);
            this.addClass('vjs-keep-tooltips-inside');
        }
        this.update(0, 0, 0);
        var progressEl = this.player_.controlBar.progressControl.el();
        progressEl.appendChild(this.tooltip);
        this.on(progressEl, 'mousemove',
            throttle(this.handleMouseMove.bind(this)), 25);
    },
    handleMouseMove: function(event){
        var range = this.player_.dvr.range();
        var time = range ? this.calculateDistance(event) *
            (range.end-range.start) + range.start : 0;
        var max_left = this.player_.controlBar.progressControl.seekBar.width()-
            this.width();
        var pos = event.pageX - el_pos(this.el().parentNode);
        pos = Math.min(Math.max(0, pos), max_left);
        var tp_width = this.tooltip.offsetWidth;
        var max_tp_left = this.tooltip.parentNode.offsetWidth-tp_width;
        var tp_pos = event.pageX-el_pos(this.tooltip.parentNode)-tp_width/2;
        tp_pos = Math.min(Math.max(0, tp_pos), max_tp_left);
        this.update(time, pos, tp_pos);
    },
    update: function(time, pos, tp_pos){
        this.el().style.left = pos + 'px';
        this.tooltip.innerHTML = this.player_.dvr.format_time(time);
        if (this.keepTooltipsInside)
        {
            var diff = pos-this.clampPosition_(pos)+1;
            var width =
                parseFloat(window.getComputedStyle(this.tooltip).width);
            this.tooltip.style.left = 'auto';
            this.tooltip.style.right = '-' +(width/2-diff)+'px';
        }
        else
        {
            this.tooltip.style.right = 'auto';
            this.tooltip.style.left = tp_pos + 'px';
        }
    },
}));

var LoadProgressBar = vjs.getComponent('LoadProgressBar');
vjs.registerComponent('DvrLoadProgressBar', vjs.extend(LoadProgressBar, {
    constructor: function(player, options){
        LoadProgressBar.call(this, player, options);
        this.partEls_ = [];
        var flashls;
        if (player.tech_.hls_obj)
            this.on(player.tech_.hls_obj, 'hlsLevelUpdated', this.update);
        else if (flashls = player.tech_.flashlsProvider)
        {
            this.on(player, 'timeupdate', function(){
                this.update(flashls.buffer);
            });
        }
    },
    update: function(buffer){
        var percentify = function(time, end){
            var percent = time / end || 0;
            return (percent >= 1 ? 1 : percent) * 100 + '%';
        };
        var range = this.player_.dvr.range();
        var start = range ? range.start : 0;
        var end = range ? range.end : 0;
        var children = this.partEls_;
        var buff = [];
        var i;
        if (buffer)
        {
            var cur = this.player_.currentTime();
            buff = [{start: cur, end: cur+buffer}];
        }
        else
        {
            var buffered = this.player_.buffered();
            for (i = 0; i < buffered.length; i++)
            {
                if (buffered.end(i)<=start || buffered.start(i)>=end)
                    continue;
                buff.push({
                    start: Math.max(buffered.start(i), start),
                    end: Math.min(buffered.end(i), end),
                });
            }
        }
        var total = buff.length ? buff[buff.length-1].end - start : 0;
        this.el_.style.width = percentify(total, end-start);
        for (i = 0; i < buff.length; i++)
        {
            children[i] = children[i] || this.el_.appendChild(vjs.createEl());
            var part = children[i];
            part.style.left = percentify(buff[i].start-start, total);
            part.style.width = percentify(buff[i].end-buff[i].start, total);
        }
        for (i = children.length; i > buff.length; i--)
            this.el_.removeChild(children[i-1]);
        children.length = buff.length;
    },
}));

var Button = vjs.getComponent('Button');
vjs.registerComponent('LiveButton', vjs.extend(Button, {
    controlText_: 'Skip back to live',
    createEl: function(){
        var el = Button.prototype.createEl.call(this, 'button', {
            className: 'vjs-live-control vjs-control',
        });
        this.contentEl_ = vjs.createEl('div', {
            className: 'vjs-live-display',
            innerHTML: this.localize('LIVE'),
        }, {
            'aria-live': 'off',
        });
        el.appendChild(this.contentEl_);
        return el;
    },
    handleClick: function(){
        this.player_.dvr.seek_to_live();
        this.player_.play();
    },
}));

},{"lodash/get":343,"lodash/throttle":375,"video.js":390}],410:[function(_dereq_,module,exports){
'use strict';
var videojs = _dereq_('video.js');
var mime = _dereq_('./mime.js');
var util = _dereq_('./util.js');
var some = _dereq_('lodash/some');

module.exports = function(){
    videojs.getComponent('Flash').registerSourceHandler({
        canHandleSource: function(source){
            if (mime.is_hls_type(source.type))
                return 'probably';
            else if (mime.is_hls_link(source.src))
                return 'maybe';
            return '';
        },
        handleSource: function(source, tech){
            if (tech.flashlsProvider)
                tech.flashlsProvider.dispose();
            return tech.flashlsProvider = new FlashlsProvider(source, tech);
        },
    }, 0);
};

function FlashlsProvider(source, tech){
    var swf = tech.el_, manual_level = -1, player_id = swf.id;
    var duration = 0, sliding_start = 0;
    var player = find_player(tech);
    var _this = this;
    this.avg_duration = 0;
    this.buffer = 0;
    function height_label(level){
        var height = level.height || Math.round(level.width * 9 / 16);
        return height ? height + 'p' : '';
    }
    function bitrate_label(level){
        return level.bitrate ? util.scaled_number(level.bitrate) + 'bps' : '';
    }
    function level_label(level, levels){
        var label = height_label(level);
        if (!label)
            return bitrate_label(level);
        var duplicated = some(levels, function(l){
            return l!=level && height_label(l)==label;
        });
        return  duplicated ? label+' '+bitrate_label(level) : label;
    }
    function switch_quality(qualityId){
        manual_level = qualityId;
        swf.vjs_setProperty('level', qualityId);
        update_quality();
    }
    function update_quality(){
        var levels = swf.hola_hls_get_levels();
        var list = [];
        if (levels.length>1)
            list.push({id: -1, label: 'Auto'});
        for (var i=0; i<levels.length; i++)
        {
            list.push({id: levels[i].index, label: level_label(levels[i],
                levels), bitrate: levels[i].bitrate});
        }
        var data = tech.quality_data = {
            quality: {
                list: list,
                selected: manual_level,
                current: swf.vjs_getProperty('level'),
            },
            callback: switch_quality,
        };
        tech.trigger('loadedqualitydata', data);
    }
    function on_msg(e){
        if (!e || !e.data || e.data.player_id!=player_id)
            return;
        switch (e.data.id)
        {
        case 'flashls.hlsEventLevelSwitch':
            update_quality();
            break;
        case 'flashls.hlsEventManifestLoaded':
            var level = e.data.levels[0];
            duration = level.duration;
            _this.avg_duration = level.averageduration;
            break;
        case 'flashls.hlsEventMediaTime':
            sliding_start = e.data.mediatime.live_sliding_main;
            duration = e.data.mediatime.duration;
            _this.buffer = e.data.mediatime.buffer;
            break;
        }
    }
    function on_hola_attach(){ player_id = swf.hola_settings({}).player_id; }
    this.seekable = function(){
        if (!duration)
            return videojs.createTimeRanges([]);
        if (!player.dvr || !swf.hola_hls_get_type ||
            swf.hola_hls_get_type()!='LIVE')
        {
            return videojs.createTimeRanges([[0, duration]]);
        }
        return videojs.createTimeRanges([[sliding_start,
            duration + sliding_start - 3*_this.avg_duration]]);
    };
    this.dispose = function(){
        window.removeEventListener('message', on_msg);
        player.off('hola.wrapper_attached', on_hola_attach);
        tech.flashlsProvider = undefined;
    };
    window.addEventListener('message', on_msg);
    player.on('hola.wrapper_attached', on_hola_attach);
    tech.setSrc(source.src);
}

function find_player(tech){
    var players = videojs.getPlayers();
    for (var key in players)
    {
        var player = players[key];
        if (player.tech_===tech)
            return player;
    }
}

},{"./mime.js":413,"./util.js":416,"lodash/some":372,"video.js":390}],411:[function(_dereq_,module,exports){
'use strict';
var videojs = window.videojs = _dereq_('video.js');
_dereq_('./css/videojs.css'); // auto injected
var mime = _dereq_('./mime.js');
var util = _dereq_('./util.js');
var id3 = _dereq_('./id3.js');
var hlsjs_source_handler = hola_vjs_provider_require();
var flashls_source_handler = _dereq_('./flashls_source_handler.js');
var url = _dereq_('url');
var _ = {
    map: _dereq_('lodash/map'),
    pick: _dereq_('lodash/pick'),
    omit: _dereq_('lodash/omit'),
    values: _dereq_('lodash/values'),
    find: _dereq_('lodash/find'),
    merge: _dereq_('lodash/merge'),
    includes: _dereq_('lodash/includes'),
};

var E = window.hola_player = module.exports = hola_player;
E.VERSION = '1.1.4';
E.players = {};
var hola_conf, customer;
try {
    /* global hola_player_api */
    hola_conf = JSON.parse(hola_player_api&&hola_player_api.zdot('json'));
} catch(e){}
hola_conf = hola_conf||{};

E.log = {
    info: log.bind(E.log, 'info'),
    warn: log.bind(E.log, 'warn'),
    error: log.bind(E.log, 'error'),
};

(function(){
    try {
        var script = util.current_script();
        customer = script && url.parse(script.src, true, true).query.customer;
        hlsjs_source_handler.attach();
        flashls_source_handler();
        load_cdn_loader();
        license_init();
    } catch(err){ E.log.error(err.stack||err); }
})();

function get_geoip(){
    var res = {};
    try {
        var json;
        if (json = localStorage.getItem('hola_geoip'))
            res = JSON.parse(json);
    } catch(e){}
    return res;
}

function parse_re(v){
    if (v.__RegExp__)
    {
        var parsed = /^\/(.*)\/(\w*)$/.exec(v.__RegExp__);
        if (!parsed)
            throw v;
        return new RegExp(parsed[1], parsed[2]);
    }
    return v;
}

function parse_fn(v){
    if (v.__Function__)
    {
        var fn_str = '"use strict"; return ('+v.__Function__+')();';
        return new Function(fn_str);
    }
    return v;
}

function get_spark_conf(){
    var conf = hola_conf&&hola_conf.spark||{};
    if (!conf.zones)
        return conf;
    var zones = _.values(conf.zones);
    zones.sort(function(a, b){ return a.order-b.order; });
    var c = _.find(zones, function(r){
        function url_match(){
            return (r.by_frame_url ? location.href : get_top_url())
                .match(parse_re(r.match));
        }
        function country_match(){
            var code =  get_geoip().country;
            var include = r.country ? r.country.include : [];
            var exclude = r.country ? r.country.exclude : [];
            return (!include.length || _.includes(include, code)) &&
                (!exclude.length || !_.includes(exclude, code));
        }
        function custom_match(){
            var fn;
            try {
                if (fn = r.is_match_allowed)
                    fn = parse_fn(fn);
            } catch(e){}
            return !fn || fn();
        }
        return url_match() && country_match() && custom_match();
    });
    c = c||conf;
    ['player', 'playlist'].forEach(function(feature){
        if ((c[feature]||{use_default_conf: true}).use_default_conf)
            c[feature] = conf[feature];
    });
    return _.merge({general: conf.general}, c);
}

function hola_player(opt, ready_cb){
    if (typeof opt=='function')
    {
        ready_cb = opt;
        opt = {};
    }
    opt = videojs.mergeOptions(opt); // clone
    var pl = opt.player && typeof opt.player!='string' && opt.player.length
        ? opt.player[0] : opt.player;
    var element = !pl ? document.querySelector('video, object, embed') :
        videojs.isEl(pl) ? pl : document.querySelector(pl);
    if (!element)
        return null;
    if (element.hola_player)
        return element.hola_player;
    if (opt = set_defaults(element, opt))
        return new Player(element, opt, ready_cb);
}

function set_defaults(element, opt){
    var next_conf, player_conf, spark_conf = get_spark_conf();
    var url = get_top_url();
    if (player_conf = spark_conf.player)
    {
        player_conf = _.omit(player_conf, 'strings');
        opt = videojs.mergeOptions(player_conf, opt);
    }
    E.debug = spark_conf.debug || opt.debug ||
        url.indexOf('hola_debug_spark')!=-1;
    opt.autoplay = opt.auto_play || opt.autoplay; // allow both
    // disable autoplay if spark autoplay is enabled
    if (spark_conf.autoplay && spark_conf.autoplay.enable)
        opt.autoplay = false;
    opt.base_url = opt.base_url||'https://player2.h-cdn.com';
    if (opt.video_url)
    {
        opt.sources = [{
            src: opt.video_url,
            type: opt.video_type||mime.guess_link_type(opt.video_url),
        }];
    }
    else if (opt.sources && !opt.sources.length)
        opt.sources = undefined;
    if (['VIDEO', 'DIV', 'OBJECT', 'EMBED'].indexOf(element.tagName)<0)
        return;
    if (element.tagName=='VIDEO')
    {
        if (!opt.sources)
        {
            var sources = element.querySelectorAll('source');
            if (!sources.length)
                return;
            opt.sources =
                Array.prototype.map.call(sources, videojs.getAttributes);
        }
        opt = videojs.mergeOptions(videojs.getAttributes(element), opt);
    }
    if (opt.sources)
    {
        opt.sources.forEach(function(s){
            s.type = s.type||mime.guess_link_type(s.src);
        });
    }
    if (opt.share===undefined)
        opt.share = {};
    if (!opt.share || opt.share.buttons && !opt.share.buttons.length)
        opt.share = undefined;
    else
        opt.share = videojs.mergeOptions(opt.share, {title: opt.title});
    if ((next_conf = spark_conf.playlist) && next_conf.next_btn &&
        ((next_conf.enable || window.hola_ve_playlist || (url &&
        url.match(/hola_ve_playlist=(1|on)/)))))
    {
        opt.next = {};
    }
    if (opt.watermark && !opt.watermark.fadeTime)
        opt.watermark.fadeTime = null;
    return opt.sources && opt;
}

function load_deps(deps){
    deps = deps||{};
    if (!window.WebVTT)
        _dereq_('videojs-vtt.js');
    _dereq_('@hola.org/videojs-osmf');
    _dereq_('@hola.org/videojs-contrib-media-sources');
    if (deps['videojs-settings'])
    {
        _dereq_('@hola.org/videojs-settings');
        _dereq_('./css/videojs-settings.css');
    }
    if (deps['videojs-hola-skin'])
    {
        _dereq_('@hola.org/videojs-hola-skin');
        _dereq_('./css/videojs-hola-skin.css');
    }
    if (deps['videojs-thumbnails'])
    {
        _dereq_('@hola.org/videojs-thumbnails');
        _dereq_('./css/videojs-thumbnails.css');
    }
    if (deps['videojs-contrib-ads'])
    {
        _dereq_('@hola.org/videojs-contrib-ads');
        _dereq_('./css/videojs-contrib-ads.css');
    }
    if (deps['videojs-ima'])
    {
        _dereq_('@hola.org/videojs-ima');
        _dereq_('./css/videojs-ima.css');
    }
    if (deps['videojs-contrib-dash'])
    {
        window.dashjs = {
            MediaPlayer: _dereq_('@hola.org/dashjs/dist/dash.mediaplayer.debug.js'),
        };
        _dereq_('videojs-contrib-dash');
    }
    if (deps.dvr)
    {
        _dereq_('./dvr.js');
        _dereq_('./css/dvr.css');
    }
    if (deps.share)
    {
        _dereq_('./share.js');
        _dereq_('./css/share.css');
    }
    if (deps.next)
    {
        _dereq_('./next.js');
        _dereq_('./css/next.css');
    }
    if (deps['videojs-watermark'])
    {
        _dereq_('videojs-watermark');
        _dereq_('./css/videojs-watermark.css');
    }
}

function Player(element, opt, ready_cb){
    E.log.info('init Spark Player v'+E.VERSION);
    this.ready_cb = ready_cb;
    this.opt = opt;
    this.element = this.init_element(element);
    this.vjs = this.init_vjs();
    this.tech_call_rewrites = {};
    this.vjs.hola = this.vjs.hola||{};
    this.vjs.hola.tech_call = this.tech_call.bind(this);
    E.players[this.id = this.vjs.id()] = this;
}

Player.prototype.init_element = function(element){
    var opt = this.opt;
    if (element.tagName=='VIDEO')
    {
        element.autoplay = false;
        element.controls = false;
        // with Spark Player wrapper there is no autoSetup mode
        element.removeAttribute('data-setup');
        if (opt.poster)
            element.poster = opt.poster;
        // XXX bahaa: find a better solution
        reset_native_hls(element, opt.sources);
    }
    else
    {
        // special case when using a div container or flash object - create
        // video tag instead
        var style = window.getComputedStyle(element);
        var attrs = {
            id: util.unique_id('hola_player'),
            class: 'video-js',
            preload: opt.preload||'auto',
        };
        var auto_height = !opt.height || isNaN(opt.height);
        var auto_width = !opt.width || isNaN(opt.width);
        if (!auto_height)
            attrs.height = opt.height||parseFloat(style.height);
        if (!auto_width)
            attrs.width = opt.width||parseFloat(style.width);
        if (opt.poster)
            attrs.poster = opt.poster;
        var videoel = videojs.createEl('video', {}, attrs);
        if (auto_width)
            videoel.style.width = '100%';
        if (auto_height)
            videoel.style.height = '100%';
        videojs.appendContent(videoel, opt.sources.map(function(source){
            return videojs.createEl('source', {}, source);
        }));
        videoel.style.position = style.position=='static' ?
            'relative' : style.position;
        videoel.style.left = style.left;
        videoel.style.top = style.top;
        // $(videoel).insertAfter(element);
        element.parentNode.insertBefore(videoel, element.nextSibling);
        element.style.display = 'none';
        element.hola_player = this;
        element = videoel;
    }
    if (!element.id)
        element.id = util.unique_id('hola_player');
    element.setAttribute('playsinline', '');
    // disable forced native controls in Facebook Instant Articles
    element.setAttribute('data-fb-disable-controls', '');
    element.hola_player = this;
    return element;
};

Player.prototype.add_languages = function(){
    var player_conf = get_spark_conf().player;
    var strings = player_conf&&player_conf.strings, langs;
    try {
        langs = strings&&JSON.parse(strings);
    } catch(e){}
    if (!langs)
        return;
    for (var key in langs)
    {
        if (langs[key] && typeof langs[key]=='object')
            videojs.addLanguage(key, langs[key]);
    }
};

Player.prototype.init_vjs = function(){
    var opt = this.opt, cb = this.ready_cb, hola_player = this;
    var vjs_opt = this.get_vjs_opt();
    load_deps({
        'videojs-settings': !!vjs_opt.plugins.settings,
        'videojs-hola-skin': !!vjs_opt.plugins.hola_skin,
        'videojs-thumbnails': !!opt.thumbnails || opt.thumbnails===undefined,
        'videojs-contrib-ads': !!opt.ads,
        'videojs-ima': !!opt.ads,
        'videojs-contrib-dash': opt.sources.some(is_dash),
        dvr: opt.dvr,
        share: opt.share,
        next: opt.next,
        'videojs-watermark': !!vjs_opt.plugins.watermark,
    });
    var element = this.element;
    this.add_languages();
    return videojs(this.element, vjs_opt, function(){
        var player = this;
        // enable tap events to make popup menu and ads work correctly
        player.emitTapEvents();
        if (player.tech_ && opt.controls)
            player.controls(true);
        if (opt.thumbnails)
            player.thumbnails(opt.thumbnails);
        hola_player.init_ads(player);
        hola_player.init_captions(player, element);
        // call init_watermark after watermark plugin was initialized
        setTimeout(function(){ hola_player.init_watermark(player, opt); });
        player.on('pause', function(e){
            if (player.scrubbing()) // XXX bahaa: do we need this?
                e.stopImmediatePropagation();
        }).on('fullscreenchange', function(e){
            var orientation = window.screen && window.screen.orientation;
            // ios doesn't support orientation api
            if (!videojs.browser.IS_ANDROID || !orientation ||
                !orientation.lock)
            {
                return;
            }
            if (player.isFullscreen())
            {
                orientation.lock(player.videoWidth()>player.videoHeight() ?
                    'landscape' : 'portrait');
            }
            else
                orientation.unlock();
        }).on('save_logs', function(e){
            // XXX bahaa: TODO
        }).on('problem_report', function(e){
            // XXX bahaa: TODO
        }).on('cdn_graph_overlay', on_cdn_graph_overlay);
        if (cb)
            try { cb(player); } catch(e){ E.log.error(e.stack||e); }
        if (opt.autoplay)
        {
            util.can_autoplay(function(res){
                E.log.info('can autoplay: '+res);
                if (!res || opt.autoplay=='sound'&&res!='sound')
                    return;
                if (res=='muted')
                    player.muted(true);
                player.play();
                player.autoplay(true);
            });
        }
    }).on('error', function (){
        var player = this;
        var error = player.error;
        if (!error || error.code!=error.MEDIA_ERR_SRC_NOT_SUPPORTED)
            return;
        var only_flash = opt.sources.every(function(s){
            return mime.is_hds_link(s.src) || mime.is_flv_link(s.src);
        });
        var flash = videojs.getTech('Flash');
        var modal = player.getChild('errorDisplay');
        if (modal && only_flash && (!flash || !flash.isSupported()))
        {
            modal.fillWith(player.localize(
                'Flash plugin is required to play this media'));
           E.log.error('flash plugin is required');
        }
    });
};

function on_cdn_graph_overlay(){
    var hola_cdn = window.hola_cdn;
    var bws = hola_cdn && hola_cdn._get_bws();
    if (window.cdn_graph || !bws || hola_cdn._get_mode()!='cdn')
        return;
    try {
        var gopt = {
            graph: 'newgraph_progress_mode_highlight_tips',
            player_obj: bws.player,
            video: bws.player.vjs
        };
        var url = 'https://player.h-cdn.com'+
            hola_cdn.require.zdot('cdngraph_js');
        hola_cdn.util.load_script(url, function(){
            window.cdn_graph.init(gopt, bws, hola_cdn); });
    } catch(err){ E.log.error(err.stack||err); }
}

Player.prototype.get_settings_opt = function(){
    var opt = this.opt, s = opt.settings;
    if (s===false)
        return;
    s = videojs.mergeOptions({
        graph: opt.graph,
        volume: opt.volume,
        embed_code: opt.embed_code,
        copy_url: opt.copy_url,
        copy_url_with_time: opt.copy_url_with_time,
    }, s);
    var sources = opt.sources && opt.sources.filter(function(source){
        return !is_adaptive(source);
    });
    if (s.quality!==false)
        s.quality = {sources: sources};
    return s;
};

Player.prototype.get_vjs_opt = function(){
    var opt = this.opt;
    var origin_opts = this.opt['data-setup']||{};
    if (origin_opts && typeof origin_opts=='string')
    {
        try {
            origin_opts = JSON.parse(origin_opts);
        } catch(e){}
    }
    // XXX: maybe we should merge all data-setup conf with vjs_opt
    origin_opts = _.pick(origin_opts, ['playbackRates']);
    var is_mobile = videojs.browser.IS_ANDROID || videojs.browser.IS_IOS;
    var class_name = is_mobile&&!opt.use_desktop_skin ? 'vjs-ios-skin' :
        undefined;
    return videojs.mergeOptions({
        sources: opt.sources,
        // XXX arik: unite swf to one
        osmf: {swf: opt.osmf_swf||
            opt.base_url+'/videojs-osmf.swf?customer='+customer
        },
        flash: {
            swf: opt.swf||opt.base_url+'/videojs.swf?customer='+customer,
            accelerated: opt.accelerated,
        },
        html5: {
            hlsjsConfig: {
                debug: false,
                fragLoadingLoopThreshold: 1000,
                manifestLoadingTimeOut: 20*1000,
                manifestLoadingMaxRetry: 4,
                levelLoadingTimeOut: 20*1000,
                levelLoadingMaxRetry: 4,
                xhrSetup: opt.withCredentials && function(xhr){
                    xhr.withCredentials = true;
                },
            },
            nativeTextTracks: false,
        },
        inactivityTimeout: opt.inactivity_timeout===undefined ?
            2000 : opt.inactivity_timeout,
        poster: opt.poster,
        loop: opt.loop,
        muted: opt.muted,
        preload: opt.preload,
        language: opt.force_language,
        techOrder:
            (opt.tech=='flash' ? ['flash', 'html5'] : ['html5', 'flash'])
            .concat('osmf'),
        tooltips: true,
        plugins: {
            settings: this.get_settings_opt(),
            dvr: opt.dvr,
            share: opt.share,
            next: opt.next,
            watermark: opt.watermark,
            hola_skin: opt.skin ? false : {
                css: false,
                className: class_name,
                show_controls_before_start: opt.show_controls_before_start,
                show_time_for_live: opt.show_time_for_live,
                play_button_color: opt.play_button_color,
                seek_bar_color: opt.seek_bar_color,
                title: opt.title,
                hide_skip_buttons: opt.hide_skip_buttons,
            },
        },
        // hola-custom component configuration
        errorDisplay: {ignoreErr: opt.hide_err_display},
    }, origin_opts, opt.videojs_options);
};

function get_existing_handler(list, method, fn) {
    if (!list || !list[method])
        return;
    return list[method].filter(function(handler){
        return handler==fn; })[0];
}

Player.prototype.add_rewrite = function(method, fn){
    var handlers = this.tech_call_rewrites[method];
    if (get_existing_handler(handlers, method, fn))
        return;
    if (!handlers)
        handlers = this.tech_call_rewrites[method] = [];
    handlers.push(fn);
};

Player.prototype.remove_rewrite = function(method, fn){
    var handlers = this.tech_call_rewrites[method];
    var existing = get_existing_handler(handlers, method, fn);
    if (!existing)
        return;
    var index = handlers.indexOf(existing);
    if (index>=0)
        handlers.splice(index, 1);
};

Player.prototype.tech_call = function(method, arg){
    var handlers = this.tech_call_rewrites[method];
    if (!handlers)
        return false;
    var preventDefault = false;
    handlers.forEach(function(handler){
        if (handler(method, arg))
            preventDefault = true;
    });
    return preventDefault;
};

Player.prototype.init_ads = function(player){
    var opt = this.opt;
    if (!opt.ads)
        return;
    if(!window.google || !window.google.ima)
    {
        util.load_script('https://imasdk.googleapis.com/js/sdkloader/ima3.js',
            this.init_ads.bind(this, player));
        E.log.info('loading imasdk...');
        return;
    }
    if (opt.ads.id3)
        opt.ads.manual = true;
    if (opt.ads.schedule)
        opt.ads.adsResponse = generate_vmap(opt.ads.schedule);
    if (!opt.ads.adTagUrl && !opt.ads.adsResponse && !opt.ads.manual)
        return E.log.error('missing Ad Tag');
    if (!window.google) // missing external <script> or blocked by AdBlocker
        return E.log.error('missing IMA HTML5 SDK');
    if (!player.ads || !player.ima) // shouldn't happen as they're bundled
        return E.log.error('missing ad modules');
    player.ima(videojs.mergeOptions({
        id: player.id(),
        vjsControls: true,
        contribAdsSettings: {
            prerollTimeout: 10000,
            postrollTimeout: 1000,
            disablePlayContentBehindAd: true,
        },
    }, opt.ads));
    E.log.info('init ima plugin');
    if (player.ima.adContainerDiv)
    {
        player.ima.adContainerDiv.style.cursor = 'pointer';
        // XXX andrey: workaround for an old android versions (4.4) where click
        // on imasdk iframe doesn't start the video. Need to check whether
        // we can enable it by default
        if (opt.ads.hideAdContainer)
            player.ima.adContainerDiv.style.display = 'none';
    }
    function init(e){
        player.off(['tap', 'click', 'play'], init);
        if (player.ima.adContainerDiv)
        {
            player.ima.adContainerDiv.style.cursor = '';
            if (opt.ads.hideAdContainer)
                player.ima.adContainerDiv.style.display = 'block';
        }
        E.log.info('init ad container');
        player.ima.initializeAdDisplayContainer();
        if (opt.ads.manual)
        {
            player.trigger('nopreroll');
            player.trigger('adsready');
        }
        else
            player.ima.requestAds();
        if (e && e.type!='play')
            player.play();
    }
    function get_ima(){
        var ima = player.ima;
        if (!ima || !ima.adsActive)
            return;
        return ima;
    }
    function handle_play_pause(method, arg){
        var ima = get_ima();
        // proceed with default action if no ima active
        if (!ima)
            return;
        if (method=='play')
        {
            if (ima.adPlaying)
                return true;
            E.log.info('resume ad');
            ima.resumeAd();
        }
        if (method=='pause')
        {
            if (!ima.adPlaying)
                return true;
            E.log.info('pause ad');
            ima.pauseAd();
        }
        return true;
    }
    this.add_rewrite('play', handle_play_pause);
    this.add_rewrite('pause', handle_play_pause);
    if (player.paused())
        player.on(['tap', 'click', 'play'], init);
    else
        init();
    if (opt.ads.id3)
        init_ads_id3(player);
};

Player.prototype.init_captions = function(player, element){
    if (!element || !element.textTracks || !player)
        return;
    var tt = element.textTracks;
    // native text tracks are not working in IE10
    if (!tt || !tt.addEventListener)
      return;
    // push native text tracks to simulated vjs text tracks
    var hook = function(obj, method, fn){
        var orig = obj[method];
        obj[method] = function(){
            fn.apply(this, arguments);
            return orig.apply(this, arguments);
        };
    };
    tt.addEventListener('addtrack', function(e){
        var track = e&&e.track;
        if (!track)
            return;
        var opt = {
            kind: track.kind,
            label: track.label,
            language: track.language,
            mode: track.mode,
        };
        opt['default'] = track['default'];
        E.log.info('add text track');
        var new_track = player.addRemoteTextTrack(opt).track;
        for (var i=0; i<track.cues.length; i++)
            new_track.addCue(track.cues[i]);
        hook(track, 'addCue', function(cue){
            for (var i=0; i<new_track.cues.length; i++)
            {
                if (new_track.cues[i].startTime==cue.startTime &&
                    new_track.cues[i].text==cue.text)
                {
                    new_track.removeCue(new_track.cues[i]);
                }
            }
            new_track.addCue(cue);
        });
        hook(track, 'removeCue', function(cue){ new_track.removeCue(cue); });
    });
};

Player.prototype.init_watermark = function(player, opt){
    var controls_opt, tooltip, img;
    if (controls_opt = opt.controls_watermark)
    {
        if (typeof controls_opt=='string')
            controls_opt = {image: controls_opt};
        var el = player.getChild('controlBar').addChild('controlsWatermark',
            controls_opt).el();
        if ((tooltip = controls_opt.tooltip) &&
            (img = el.querySelector('img')))
        {
            img.alt = tooltip;
        }
    }
    if (!(tooltip = opt.watermark && opt.watermark.tooltip) ||
        !(img = player.el().querySelector('.vjs-watermark-content img')))
    {
        return;
    }
    img.alt = tooltip;
    img.title = tooltip;
};

function format_time(t){
    if (!isFinite(t))
        return;
    var hh = Math.floor(t/3600);
    t = t%3600;
    var mm = Math.floor(t/60);
    var ss = t%60;
    hh = hh<10 ? '0'+hh : hh;
    mm = mm<10 ? '0'+mm : mm;
    ss = ss<10 ? '0'+ss.toFixed(3) : ss.toFixed(3);
    return hh+':'+mm+':'+ss;
}

function generate_vmap(schedule){
    if (!Array.isArray(schedule))
        return void E.log.error('ads.schedule param must be an array');
    var vmap = '<?xml version="1.0" encoding="UTF-8"?>\n<vmap:VMAP '+
        'xmlns:vmap="http://www.iab.net/videosuite/vmap" version="1.0">\n';
    for (var i=0; i<schedule.length; i++)
    {
        var ad = schedule[i];
        var time = ad.time=='start'||ad.time=='end' ? ad.time :
            format_time(ad.time);
        if (!time)
            return void E.log.error('wrong ad time format');
        if (!ad.tag)
            return void E.log.error('ad tag not found');
        var type = ad.type || 'linear,nonlinear';
        vmap +=
        ' <vmap:AdBreak timeOffset="'+time+'" breakType="'+type+'">\n'+
        '  <vmap:AdSource allowMultipleAds="false" followRedirects="true">\n'+
        '   <vmap:AdTagURI templateType="vast3">\n'+
        '    <![CDATA['+ad.tag+']]>\n'+
        '   </vmap:AdTagURI>\n'+
        '  </vmap:AdSource>\n'+
        ' </vmap:AdBreak>\n';
    }
    return vmap+'</vmap:VMAP>';
}

function init_ads_id3(player){
    var cues = [], played_cues = {};
    player.on('timeupdate', function(){
        var cur = player.currentTime();
        cues.forEach(function(cue){
            if (played_cues[cue.time] || cur<cue.time || cur-cue.time>0.5)
                return;
            player.ima.playAd(cue.ad);
            played_cues[cue.time] = true;
        });
    });
    player.tech_.on('parsedmetadata', function(e, data){
        var sample = data && data.samples && data.samples[0];
        var tags = id3.parse_id3(sample.data||sample.unit);
        var ad = tags.TXXX && tags.TXXX.adID;
        if (ad && cues.indexOf(sample.dts)<0)
        {
            cues.push({ad: ad, time: sample.dts});
            player.trigger('ads-cuepoints', _.map(cues, 'time'));
        }
    });
}

function is_hls(s){
    return mime.is_hls_link(s.src) || mime.is_hls_type(s.type);
}

function is_dash(s){
    return mime.is_dash_link(s.src) || mime.is_dash_type(s.type);
}

function is_hds(s){
    return mime.is_hds_link(s.src) || mime.is_hds_type(s.type);
}

function is_adaptive(s){
    return is_hls(s) || is_dash(s) || is_hds(s);
}

function reset_native_hls(el, sources){
    // not using el.currentSrc because it might not be selected yet.
    if (!el.canPlayType('application/x-mpegurl') || !sources.some(is_hls))
        return;
    // if source is hls and browser supports hls natively reset video element
    // so videojs will select our hls source handler instead of native.
    el.src = '';
    el.load();
}

function load_cdn_loader(){
    if (!customer)
        return;
    if (document.querySelector('script[src*="//player.h-cdn.com/loader"]'))
    {
        E.log.warn('Spark loader.js is included with Spark Player. '
            +'There is no need to load it separately');
        return;
    }
    E.log.info('Adding CDN loader...');
    util.load_script('https://player.h-cdn.com/loader.js?customer='+customer,
        undefined, {async: true, crossOrigin: 'anonymous'});
}

function license_init(){
    if (Math.random()>0.1)
        return;
    var xhr = new XMLHttpRequest();
    var script = util.current_script();
    var opt = {
        v: E.VERSION,
        loader: window.hola_cdn && window.hola_cdn.ver,
        hls: window.Hls && window.Hls.version,
        customer: customer,
        hosted: !script || !script.src.match(/^(https?:)?\/\/\w*\.h-cdn\.com/),
        url: get_top_url(),
    };
    var qs = Object.keys(opt)
        .filter(function(key){ return !!opt[key]; })
        .map(function(key){ return key+'='+encodeURIComponent(opt[key]); })
        .join('&');
    xhr.open('GET', 'https://perr.h-cdn.com/hola_player/license_init?'+qs);
    xhr.send();
}

function pad(num, size){
    return ('000'+num).slice(-size);
}

function date_str(){
    var d = new Date();
    return pad(d.getUTCFullYear(), 4)+'-'+pad(d.getUTCMonth()+1, 2)
    +'-'+pad(d.getUTCDate(), 2)
    +' '+pad(d.getUTCHours(), 2)+':'+pad(d.getUTCMinutes(), 2)
    +':'+pad(d.getUTCSeconds(), 2)
    +'.'+pad(d.getUTCMilliseconds(), 3);
}

function log(type, msg){
    if (type=='info' && !E.debug)
        return;
    console[type=='info' ? 'log' : type](date_str()+' hola_player: '+msg);
}

function get_top_url(){
    return window.top==window ? location.href : document.referrer;
}

},{"./css/dvr.css":399,"./css/next.css":400,"./css/share.css":401,"./css/videojs-contrib-ads.css":402,"./css/videojs-hola-skin.css":403,"./css/videojs-ima.css":404,"./css/videojs-settings.css":405,"./css/videojs-thumbnails.css":406,"./css/videojs-watermark.css":407,"./css/videojs.css":408,"./dvr.js":409,"./flashls_source_handler.js":410,"./id3.js":412,"./mime.js":413,"./next.js":414,"./share.js":415,"./util.js":416,"@hola.org/dashjs/dist/dash.mediaplayer.debug.js":1,"@hola.org/videojs-contrib-ads":6,"@hola.org/videojs-contrib-media-sources":18,"@hola.org/videojs-hola-skin":43,"@hola.org/videojs-ima":44,"@hola.org/videojs-osmf":45,"@hola.org/videojs-settings":167,"@hola.org/videojs-thumbnails":168,"lodash/find":340,"lodash/includes":346,"lodash/map":365,"lodash/merge":367,"lodash/omit":369,"lodash/pick":370,"lodash/values":381,"url":388,"video.js":390,"videojs-contrib-dash":391,"videojs-vtt.js":392,"videojs-watermark":398}],412:[function(_dereq_,module,exports){
'use strict';
var E = module.exports;

function read_int(data, offset){
    return data[offset++]<<21 | data[offset++]<<14 | data[offset++]<<7 |
        data[offset++];
}

function read_utf(data, start, len){
    var res = '';
    for (var i=start; i<start+len; i++)
        res += String.fromCharCode(data[i]);
    return res;
}

function parse_txxx(data){
    var res = {};
    var i = data.indexOf(0);
    if (data[0]!=3 || i<0)
        return res;
    res[read_utf(data, 1, i-1)] = read_utf(data, i+1, data.length-i-1);
    return res;
}

function parse_frames(data){
    var offset = 0, id, size, res = {};
    while (offset+8 <= data.length)
    {
        id = read_utf(data, offset, 4);
        offset += 4;
        size = read_int(data, offset);
        offset += 6;
        if (id=='TXXX')
            res.TXXX = parse_txxx(data.subarray(offset, offset+size));
        offset += size;
    }
    return res;
}

E.parse_id3 = function(data){
    if (!data || !data.length)
        return {};
    var offset = 0, size, header;
    header = read_utf(data, offset, 3);
    if (header!='ID3')
        return {};
    offset += 6;
    size = read_int(data, offset);
    offset += 4;
    return parse_frames(data.subarray(offset, offset+size));
};

},{}],413:[function(_dereq_,module,exports){
'use strict';
var url = _dereq_('url');
var E = module.exports;

var mp4_suffix = /\.(mp4|m4p|m4v|mov)$/i;
var hls_suffix = /\.m3u8$/;
var hds_suffix = /\.f4m$/;
var dash_suffix = /\.mpd$/;
var flv_suffix = /\.flv$/;
var webm_suffix = /\.webm$/;
function is_rx_link(v, rx){
    return !!v && rx.test(url.parse(v).pathname.split(';')[0]); }
E.is_mp4_link = function(v){ return is_rx_link(v, mp4_suffix); };
E.is_hls_link = function(v){ return is_rx_link(v, hls_suffix); };
E.is_hds_link = function(v){ return is_rx_link(v, hds_suffix); };
E.is_dash_link = function(v){ return is_rx_link(v, dash_suffix); };
E.is_flv_link = function(v){ return is_rx_link(v, flv_suffix); };
E.is_webm_link = function(v){ return is_rx_link(v, webm_suffix); };
E.guess_link_type = function(v){
    var p = url && url.parse(v).pathname;
    if (mp4_suffix.test(p))
        return 'video/mp4';
    if (hls_suffix.test(p))
        return 'application/x-mpegurl';
    if (hds_suffix.test(p))
        return 'application/adobe-f4m';
    if (dash_suffix.test(p))
        return 'application/dash+xml';
    if (flv_suffix.test(p))
        return 'video/flv';
    if (webm_suffix.test(p))
        return 'video/webm';
    console.log('could not guess link type: "'+v+'" assuming mp4');
    return 'video/mp4';
};
E.is_hls_type = function(type){
    return /^application\/x-mpegurl$/i.test(type);
};
E.is_hds_type = function(type){
    return /^application\/adobe-f4m$/i.test(type);
};
E.is_dash_type = function(type){
    return /^application\/dash\+xml/i.test(type);
};

},{"url":388}],414:[function(_dereq_,module,exports){
'use strict';
var vjs = _dereq_('video.js');

vjs.plugin('next', function(opt){
    var player = this;
    player.ready(function(){
        var b, controlBar = player.controlBar;
        b = controlBar.nextVideo = controlBar.addChild('nextButton', opt||{});
        b.on('mouseenter', function(){
            b.addClass('vjs-next-suggestion-show');
            player.trigger('next_suggestion_show');
        });
        b.on('mouseleave', function(){
            b.removeClass('vjs-next-suggestion-show');
            player.trigger('next_suggestion_hide');
        });
    });
});
var events = ['touchstart', 'touchend', 'click'];
var prevent_click = function(event){
    event.stopPropagation();
};
var Button = vjs.getComponent('Button');
vjs.registerComponent('NextButton', vjs.extend(Button, {
    controlText_: 'Next',
    constructor: function(player, options){
        Button.call(this, player, options);
        this.on(events, prevent_click);
    },
    createEl: function(){
        var el = Button.prototype.createEl.apply(this, arguments);
        el.appendChild(vjs.createEl('div', {
            className: 'vjs-button-icon',
            innerHTML: "<svg fill=\"#FFFFFF\" height=\"48\" viewBox=\"0 0 24 24\" width=\"48\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path d=\"M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z\"/>\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\n</svg>",
        }));
        return el;
    },
    buildCSSClass: function(){
        return 'vjs-next-button '+Button.prototype.buildCSSClass.call(this);
    },
    handleClick: function(event){
        this.player_.trigger('next_suggestion_play'); },
}));

},{"video.js":390}],415:[function(_dereq_,module,exports){
'use strict';
var vjs = _dereq_('video.js');

var services = {
    facebook: {
        text: 'Facebook',
        link: 'https://www.facebook.com/sharer/sharer.php?u={url}',
        svg: "<svg viewBox=\"0 0 48 48\">\n  <rect fill=\"#3B5898\" width=\"100%\" height=\"100%\"></rect>\n  <path fill=\"#FFFFFF\" d=\"M39.4,29.4l0.9-7.2h-7.2v-4.6c0-2.1,0.6-3.5,3.6-3.5h3.8V7.5c-0.7-0.1-2.9-0.3-5.6-0.3 c-5.5,0-9.3,3.4-9.3,9.6v5.3h-6.3v7.2h6.3V48h7.5V29.4H39.4z\"></path>\n</svg>\n",
        popup: {width: 560, height: 610},
    },
    twitter: {
        text: 'Twitter',
        link: 'https://twitter.com/intent/tweet?url={url}&text={title}',
        svg: "<svg viewBox=\"0 0 48 48\">\n  <rect fill=\"#55ACEE\" width=\"100%\" height=\"100%\"></rect>\n  <path fill=\"#FFFFFF\" d=\"M40,14.1c-1.2,0.5-2.4,0.9-3.8,1c1.4-0.8,2.4-2.1,2.9-3.6c-1.3,0.8-2.7,1.3-4.2,1.6 C33.8,11.8,32,11,30.2,11c-3.6,0-6.6,2.9-6.6,6.6c0,0.5,0.1,1,0.2,1.5c-5.5-0.3-10.3-2.9-13.5-6.9c-0.6,1-0.9,2.1-0.9,3.3 c0,2.3,1.2,4.3,2.9,5.5c-1.1,0-2.1-0.3-3-0.8v0.1c0,3.2,2.3,5.8,5.3,6.4c-0.6,0.1-1.1,0.2-1.7,0.2c-0.4,0-0.8,0-1.2-0.1 c0.8,2.6,3.3,4.5,6.1,4.6c-2.2,1.8-5.1,2.8-8.2,2.8c-0.5,0-1.1,0-1.6-0.1C10.9,36,14.4,37,18.1,37c12.1,0,18.7-10,18.7-18.7 c0-0.3,0-0.6,0-0.8C38,16.6,39.1,15.4,40,14.1z\"></path>\n</svg>\n",
        popup: {width: 500, height: 260},
    },
    'google+': {
        text: 'Google+',
        link: 'https://plus.google.com/share?url={url}',
        svg: "<svg viewBox=\"0 0 48 48\">\n  <rect fill=\"#DB4437\" width=\"100%\" height=\"100%\"></rect>\n  <polyline fill=\"#FFFFFF\" points=\"42.1,21.8 37.7,21.8 37.7,17.4 34.4,17.4 34.4,21.8 30,21.8 30,25.1 34.4,25.1 34.4,29.5 37.7,29.5 37.7,25.1 42.1,25.1\"></polyline>\n  <path fill=\"#FFFFFF\" d=\"M16.8,26.1V22h10.3c0.2,0.7,0.3,1.3,0.3,2.3c0,6.3-4.2,10.7-10.6,10.7c-6.1,0-11-4.9-11-11s4.9-11,11-11 c3,0,5.5,1.1,7.4,2.9l-3.1,3c-0.8-0.7-2.2-1.6-4.2-1.6c-3.6,0-6.6,3-6.6,6.7s3,6.7,6.6,6.7c4.2,0,5.8-2.9,6.1-4.6H16.8z\"></path>\n</svg>\n",
        popup: {width: 400, height: 500},
    },
    blogger: {
        text: 'Blogger',
        link: 'https://www.blogger.com/blog-this.g?u={url}&n={title}',
        svg: "<svg viewBox=\"0 0 48 48\">\n  <rect fill=\"#FF9800\" width=\"100%\" height=\"100%\"></rect>\n  <path fill=\"#F1F1F1\" d=\"M37.1,20.2h-1.2c-1,0-1.9-0.8-1.9-1.9c0-5.2-4.2-9.4-9.4-9.4h-6.2C13.2,9,9,13.2,9,18.4v11.2 c0,5.2,4.2,9.4,9.4,9.4h11.2c5.2,0,9.4-4.2,9.4-9.4v-7.5C39,21.1,38.2,20.2,37.1,20.2z M18.4,16.5h7.5c1,0,1.9,0.8,1.9,1.9 s-0.8,1.9-1.9,1.9h-7.5c-1,0-1.9-0.8-1.9-1.9S17.3,16.5,18.4,16.5z M29.6,31.5H18.4c-1,0-1.9-0.8-1.9-1.9s0.8-1.9,1.9-1.9h11.2 c1,0,1.9,0.8,1.9,1.9S30.7,31.5,29.6,31.5z\"></path>\n</svg>\n",
        popup: {width: 705, height: 455},
    },
    reddit: {
        text: 'Reddit',
        link: 'https://reddit.com/submit?url={url}',
        svg: "<svg viewBox=\"0 0 48 48\">\n  <rect fill=\"#CEE3F8\" width=\"100%\" height=\"100%\"></rect>\n  <circle fill=\"#FFFFFF\" cx=\"35.5\" cy=\"13.2\" r=\"2.5\"></circle>\n  <path fill=\"#FFFFFF\" d=\"M40.2,23.7c0-1.7-1.4-3.1-3.2-3.1c-1,0-1.9,0.5-2.5,1.2c-2.6-1.8-6.4-3-10.6-3s-7.9,1.1-10.6,2.9 c-0.6-0.7-1.5-1.1-2.4-1.1c-1.8,0-3.2,1.4-3.2,3.1c0,1.3,0.8,2.4,1.9,2.9C9.5,27,9.5,27.5,9.5,28c0,5.1,6.5,9.3,14.5,9.3 s14.5-4.1,14.5-9.3c0-0.5-0.1-0.9-0.2-1.4C39.4,26.2,40.2,25,40.2,23.7z\"></path>\n  <path fill=\"#000000\" d=\"M41,23.7c0-2.1-1.8-3.9-3.9-3.9c-1,0-1.9,0.4-2.6,1c-2.6-1.6-5.9-2.6-9.4-2.7l1.9-5.9l5.2,1.2c0.1,1.7,1.5,3.1,3.3,3.1 s3.3-1.4,3.3-3.2s-1.5-3.2-3.3-3.2c-1.3,0-2.4,0.8-3,1.9l-5.9-1.4c-0.4-0.1-0.8,0.1-0.9,0.5l-2.3,7c-3.8,0.1-7.3,1-10,2.7 c-0.7-0.6-1.6-0.9-2.5-0.9c-2.2,0-3.9,1.7-3.9,3.9c0,1.4,0.7,2.6,1.8,3.2c-0.1,0.4-0.1,0.7-0.1,1.1c0,2.7,1.6,5.3,4.6,7.2 C16.1,37,19.9,38,23.9,38s7.8-1,10.6-2.8c3-1.9,4.6-4.4,4.6-7.2c0-0.3,0-0.7-0.1-1C40.2,26.3,41,25.1,41,23.7z M35.5,11.5 c1,0,1.8,0.8,1.8,1.7c0,0.9-0.8,1.7-1.8,1.7s-1.8-0.8-1.8-1.7C33.7,12.3,34.5,11.5,35.5,11.5z M8.5,23.7c0-1.3,1.1-2.4,2.4-2.4 c0.5,0,0.9,0.1,1.2,0.3c-1.4,1.1-2.4,2.4-2.9,3.8C8.8,25,8.5,24.4,8.5,23.7z M33.7,33.9c-2.6,1.7-6.1,2.6-9.8,2.6s-7.2-0.9-9.8-2.6 c-2.5-1.6-3.9-3.7-3.9-5.9c0-0.2,0-0.3,0-0.5c0-0.5,0.2-1,0.3-1.4c0.5-1.2,1.3-2.3,2.6-3.3c0.3-0.2,0.6-0.5,0.9-0.7 c0.1-0.1,0.2-0.1,0.3-0.2c2.6-1.6,5.9-2.4,9.5-2.4s7,0.9,9.6,2.5l0.2,0.1c0.4,0.2,0.7,0.5,1,0.8c1.2,1,2,2.1,2.5,3.2 c0.2,0.5,0.3,1,0.3,1.4c0,0.2,0,0.3,0,0.5C37.6,30.2,36.3,32.3,33.7,33.9z M38.6,25.5c-0.5-1.4-1.5-2.7-2.9-3.8 c0.4-0.2,0.8-0.4,1.3-0.4c1.3,0,2.4,1.1,2.4,2.4C39.5,24.4,39.2,25.1,38.6,25.5z\"></path>\n  <path fill=\"#FF4500\" d=\"M18.9,23.9c-1.3,0-2.4,1.1-2.4,2.3s1.1,2.3,2.4,2.3s2.3-1,2.3-2.3S20.2,23.9,18.9,23.9z\"></path>\n  <path fill=\"#FF4500\" d=\"M29.1,23.9c-1.3,0-2.4,1.1-2.4,2.3c0,1.3,1.1,2.3,2.4,2.3s2.3-1,2.3-2.3S30.4,23.9,29.1,23.9z\"></path>\n  <path fill=\"#000000\" d=\"M29.5,31.7c-0.3-0.3-0.8-0.3-1.1,0C27.6,32.6,26.1,33,24,33s-3.6-0.4-4.4-1.3c-0.3-0.3-0.8-0.3-1.1,0c-0.3,0.3-0.3,0.8,0,1 c1.2,1.1,3,1.7,5.5,1.7s4.3-0.6,5.5-1.7C29.8,32.5,29.8,32,29.5,31.7z\"></path>\n</svg>\n",
        popup: {width: 860, height: 770},
    },
    tumblr: {
        text: 'Tumblr',
        link: 'https://www.tumblr.com/widgets/share/tool?canonicalUrl={url}',
        svg: "<svg viewBox=\"0 0 48 48\">\n  <rect fill=\"#385874\" width=\"100%\" height=\"100%\"></rect>\n  <path fill=\"#FFFFFF\" d=\"M25.3,7v8.6h9.3v5.3h-9.3v8.7c0,2,0.1,3.2,0.3,3.8c0.2,0.6,0.6,1,1.2,1.3c0.8,0.5,1.7,0.7,2.7,0.7 c1.8,0,3.6-0.6,5.4-1.7v5.4c-1.5,0.7-2.9,1.2-4.1,1.4c-1.2,0.3-2.6,0.4-4,0.4c-1.6,0-3.1-0.2-4.3-0.6c-1.3-0.4-2.3-1-3.2-1.7 c-0.9-0.7-1.5-1.5-1.8-2.3s-0.5-2-0.5-3.6V20.9h-4v-4.8c1.4-0.4,2.6-1.1,3.6-1.9c1-0.8,1.8-1.8,2.4-2.9c0.6-1.1,1-2.6,1.2-4.3H25.3 z\"></path>\n</svg>\n",
        popup: {width: 600, height: 500},
    },
    vk: {
        text: 'VK',
        link: 'https://vk.com/share.php?url={url}',
        svg: "<svg viewBox=\"0 0 48 48\">\n  <rect fill=\"#42678E\" width=\"100%\" height=\"100%\"></rect>\n  <path fill=\"#FFFFFF\" d=\"M34.6,33.7h-0.2c-1.7-0.3-2.9-1.5-3.9-2.6c-0.6-0.6-1.4-2.1-2.5-1.9c-1.5,0.2-0.7,2.7-1.4,3.8 c-0.5,0.9-2.6,0.7-4.1,0.6c-4.6-0.5-6.9-2.9-9-5.5c-2.2-2.8-3.9-5.7-5.5-9C7.7,18,7,16.9,7,15.6c0.7-0.6,2-0.4,3.2-0.4 c1,0,2.7-0.2,3.4,0.2c0.5,0.2,0.9,1.4,1.3,2.2c0.8,1.8,1.6,3.1,2.6,4.5c0.4,0.6,1.1,1.8,1.9,1.7c0.8-0.2,0.8-2,0.9-3.2 c0-1.4,0-3.1-0.4-3.8c-0.4-0.7-1.1-0.7-1.8-1.1c0.6-1.2,2.2-1.3,4.1-1.3c1.6,0,3.8-0.2,4.5,0.9c0.6,1.1,0.2,3,0.2,4.4 c0,1.6-0.3,3.3,0.8,4c0.9-0.1,1.3-0.9,1.8-1.6c1.1-1.4,1.8-2.7,2.6-4.5c0.3-0.8,0.7-2.1,1.4-2.3c1-0.4,2.5-0.1,3.9-0.1 c1.2,0,3.2-0.3,3.6,0.5c0.3,0.7-0.6,2.3-0.9,2.8c-1.1,1.7-2.1,2.8-3.3,4.4c-0.5,0.6-1.4,1.6-1.4,2.5c0,0.7,0.8,1.3,1.4,1.8 c1.2,1.1,2.1,2,3.1,3.2c0.4,0.5,1.2,1.5,1.1,2.1C40.8,34.5,36.2,33.2,34.6,33.7z\"></path>\n</svg>\n",
        popup: {width: 650, height: 580},
    },
    linkedin: {
        text: 'LinkedIn',
        link: 'https://www.linkedin.com/shareArticle?url={url}&title={title}',
        svg: "<svg viewBox=\"0 0 48 48\">\n  <rect fill=\"#0080B2\" width=\"100%\" height=\"100%\"></rect>\n  <path fill=\"#FFFFFF\" d=\"M9.4,39h6.4V18.8H9.4V39z M12.6,9C10.4,9,9,10.5,9,12.5c0,1.9,1.4,3.5,3.6,3.5l0,0c2.2,0,3.6-1.6,3.6-3.5 C16.2,10.5,14.8,9,12.6,9z M31.6,18.3c-3.4,0-4.9,2-5.8,3.3v0.1l0,0v-0.1v-2.9h-6.4c0.1,1.9,0,20.2,0,20.2h6.4V27.7 c0-0.6,0-1.2,0.2-1.6c0.5-1.2,1.5-2.5,3.3-2.5c2.3,0,3.3,1.9,3.3,4.6V39H39V27.4C39,21.2,35.8,18.3,31.6,18.3z\"></path>\n</svg>\n",
        popup: {width: 550, height: 470},
    },
    email: {
        text: 'Email',
        link: 'mailto:?body={url}',
        svg: "<svg viewBox=\"0 0 24 24\">\n  <rect fill=\"#888\" width=\"100%\" height=\"100%\"/>\n  <path fill=\"#fff\" d=\"M21,8.5l-9,5l-9-5v-2l9,5l9-5 M20,5.5H4c-0.5,0-1,0.4-1,0.8v11.3c0,0.5,0.4,0.8,1,0.8H20c0.5,0,1-0.4,1-0.8V6.3 C21,5.9,20.6,5.5,20,5.5z\"/>\n</svg>\n\n",
    },
};

vjs.plugin('share', function(opt){
    var player = this;
    player.ready(function(){
        player.addChild('ShareButton', opt||{});
    });
});
var events = ['touchstart', 'touchend', 'click'];
var prevent_click = function(event){
    event.stopPropagation();
};
function get_top_url(){
    return window.top==window ? location.href : document.referrer;
}
var Button = vjs.getComponent('Button');
vjs.registerComponent('ShareButton', vjs.extend(Button, {
    controlText_: 'Share video',
    constructor: function(player, options){
        Button.call(this, player, options);
        this.on(events, prevent_click);
    },
    createEl: function(){
        var el = Button.prototype.createEl.apply(this, arguments);
        el.appendChild(vjs.createEl('div', {
            className: 'vjs-button-icon',
            innerHTML: "<svg fill=\"#FFF\" viewBox=\"0 0 24 24\">\n  <path d=\"M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z\"/>\n</svg>\n",
        }));
        return el;
    },
    buildCSSClass: function(){
        return 'vjs-share-button '+Button.prototype.buildCSSClass.call(this);
    },
    handleClick: function(event){
        var player = this.player_;
        var modal = player.addChild('shareDialog', this.options_);
        this.on(modal, 'dispose', function(){
            player.removeChild(modal);
            this.show();
        });
        modal.open();
        this.hide();
    },
}));
var ModalDialog = vjs.getComponent('ModalDialog');
vjs.registerComponent('ShareDialog', vjs.extend(ModalDialog, {
    constructor: function(player, options){
        ModalDialog.call(this, player, options);
        this.on(events, prevent_click);
        this.on(this.getChild('closeButton'), events, prevent_click);
        this.options_.fillAlways = false;
        this.hasBeenFilled_ = true;
        var el = this.contentEl_;
        el.appendChild(vjs.createEl('div', {
            className: 'vjs-share-dialog-title',
            innerHTML: this.localize('Share video'),
        }));
        var btns_el = vjs.createEl('div', {
            className: 'vjs-share-dialog-buttons',
        });
        el.appendChild(btns_el);
        this.contentEl_ = btns_el;
        var all_btns = Object.keys(services);
        var buttons = options.buttons || all_btns;
        var _this = this;
        buttons.forEach(function(b){
            if (all_btns.indexOf(b)==-1)
                return;
            _this.addChild('ShareLink', vjs.mergeOptions(_this.options_,
                {type: b}));
        });
        this.contentEl_ = el;
    },
    buildCSSClass: function(){
        return 'vjs-share-dialog '+
            ModalDialog.prototype.buildCSSClass.call(this);
    },
}));
var ClickableComponent = vjs.getComponent('ClickableComponent');
vjs.registerComponent('ShareLink', vjs.extend(ClickableComponent, {
    constructor: function(player, options){
        ClickableComponent.call(this, player, options);
        this.on(events, prevent_click);
        this.type_ = options.type;
        var item = services[this.type_];
        this.controlText(item.text);
        this.addClass('vjs-share-'+this.type_);
        var url = this.options_.url||get_top_url();
        this.href = item.link
            .replace('{url}', encodeURIComponent(url))
            .replace('{title}', encodeURIComponent(this.options_.title||''));
        this.link = vjs.createEl('a', {className: 'vjs-share-link',
            innerHTML: item.svg}, {target: this.type_!='email' ? '_blank' : '',
            href: this.href});
        this.link.addEventListener('touchstart', function(e){
            e.stopPropagation(); });
        this.link.addEventListener('click', this.on_click.bind(this));
        this.el_.appendChild(this.link);
    },
    on_click: function(e){
        var item = services[this.type_], popup;
        if (!(popup = item.popup))
            return;
        e.preventDefault();
        window.open(this.href, this.type_,'left=20,top=20,height='+
            popup.height+',width='+popup.width+',resizable=1');
    },
}));

},{"video.js":390}],416:[function(_dereq_,module,exports){
'use strict';
var videojs = window.videojs;
var E = module.exports;

// https://github.com/mathiasbynens/small/blob/master/mp4-with-audio.mp4
E.small_mp4_with_sound = 'data:video/mp4;base64,AAAAHGZ0eXBpc29tAAACAGlzb21pc2'
+'8ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
+'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
+'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
+'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
+'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
+'AAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
+'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG/'
+'/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
+'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
+'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
+'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
+'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAA'
+'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
+'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAA'
+'AALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAA'
+'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAA'
+'IAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAE'
+'AAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAA'
+'AAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAA'
+'AAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcm'
+'VmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAA'
+'AAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAf'
+'QAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAA'
+'ABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAA'
+'EAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAA'
+'AAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE=';

E.load_script = function(url, onload, attrs){
    var script = document.createElement('script');
    script.src = url;
    script.onload = onload;
    if (attrs)
        Object.assign(script, attrs);
    if (document.getElementsByTagName('head').length)
        document.getElementsByTagName('head')[0].appendChild(script);
    else if (document.getElementsByTagName('body').length)
        document.getElementsByTagName('body')[0].appendChild(script);
    else if (document.head)
        document.head.appendChild(script);
};

E.current_script = function(){
    var script;
    if (script = document.querySelector(
        'script[src*="//player.h-cdn.com/player/"]') ||
        document.querySelector('script[src*="//player2.h-cdn.com/"]') ||
        document.querySelector(
        'script[src*="//cdn.jsdelivr.net/hola_player/"]') ||
        document.querySelector(
        'script[src*="//cdn.jsdelivr.net/npm/@hola.org/hola_player@"]'))
    {
        return script;
    }
    if (script = document.currentScript || current_script_ie())
        return script;
    // assumes wasn't loaded async
    var scripts = document.getElementsByTagName('script');
    return scripts[scripts.length-1];
};

function current_script_ie(){
    try { throw new Error(); }
    catch(err) {
      var res = (/.*at [^\(]*\((.*):.+:.+\)$/ig).exec(err.stack);
      if (res)
          return document.querySelector('script[src="'+res[1]+'"]');
    }
}

var id_counter = 0;
var rand = Math.floor(Math.random()*10000)+'';
E.unique_id = function(prefix){
    return (prefix ? prefix+'_' : '')+rand+'_'+(++id_counter);
};

E.scaled_number = function(num){
    if (num===undefined)
        return '';
    if (!num)
        return '0';
    var k = 1024;
    var sizes = ['', 'K', 'M', 'G', 'T', 'P'];
    var i = Math.floor(Math.log(num)/Math.log(k));
    num /= Math.pow(k, i);
    if (num<0.001)
        return '0';
    if (num>=k-1)
        num = Math.trunc(num);
    var str = num.toFixed(num<1 ? 3 : num<10 ? 2 : num<100 ? 1 : 0);
    return str.replace(/\.0*$/, '')+sizes[i];
};

function try_play(v, cb){
    var p = v.play();
    if (p&&p.then)
    {
        p.then(function(){ cb(); }, function(error){ cb(error); });
        return;
    }
    if (v.paused)
        return cb('play refused');
    var listener = function(e){
        v.removeEventListener('playing', listener);
        v.removeEventListener('error', listener);
        v.removeEventListener('abort', listener);
        v.removeEventListener('pause', listener);
        if (e.type=='playing')
            return cb();
        var err = 'not playing: '+e.type, msg;
        if (e.type=='error' && (msg = v.error&&v.error.message))
            err += ', '+msg;
        cb(err);
    };
    v.addEventListener('playing', listener);
    v.addEventListener('error', listener);
    v.addEventListener('abort', listener);
    v.addEventListener('pause', listener);
}

E.can_autoplay = function(cb, muted, force){
    // IE older than 10.0 doesn't support this test
    if (videojs.browser.IE_VERSION && videojs.browser.IE_VERSION<10)
        return 'sound';
    if (E._can_autoplay!==undefined && !force)
    {
        if (Array.isArray(E._can_autoplay))
            return void E._can_autoplay.push(cb);
        return void cb(E._can_autoplay);
    }
    E._can_autoplay = E._can_autoplay||[];
    var v = document.createElement('video');
    v.muted = muted;
    v.setAttribute('muted', muted);
    v.setAttribute('playsinline', 1);
    v.src = E.small_mp4_with_sound;
    try_play(v, function(error){
        var res;
        if (!error)
            res = muted ? 'muted' : 'sound';
        else if (muted)
            res = false;
        if (res!==undefined)
        {
            var callbacks = E._can_autoplay;
            E._can_autoplay = res;
            cb(res);
            if (!Array.isArray(callbacks))
                return;
            for (var i=0; i<callbacks.length; i++)
                callbacks[i](res);
            return;
        }
        setTimeout(function(){ E.can_autoplay(cb, true, true); });
    });
};

},{}]},{},[411])(411)
});

};
hola_vjs_provider_require = function(){
var res, define = function(name, fn){res=fn();};
define.amd = true;
// REQUIRE_START: hola_videojs_hls.js
(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.hola_player = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_dereq_,module,exports){
var zdot_conf = _dereq_('./zdot_conf.js');
var external_util = _dereq_('./external_util.js');
var E = {};
var ls;
try { ls = typeof window!='undefined' && window.localStorage; }
catch(e){}
var urls = [];
if (typeof window!='undefined')
{
    if (window.top!=window)
        urls.push(document.referrer);
    urls.push(location.href);
}

function get_str_conf(key, s){
    var m = s.match(new RegExp('[?&#]'+key+'(=.*?)?(#|&|$)'));
    return m && (m[1] ? m[1].replace(/^=/, '') : '');
}

function get_url_conf(key){
    var m;
    urls.forEach(function(u){ m = m||get_str_conf(key, u); });
    return m;
}

function get_conf(key, def_empty){
    var res, hp_key = 'hola_provider_'+key;
    if ((res = get_url_conf(hp_key))!=null)
        console.info(E.provider_id+': using '+hp_key+' from url');
    else if (ls && (res = ls['hola_provider_'+key])!=undefined)
        console.info(E.provider_id+': using '+hp_key+' from lstorage');
    else if (E.owner && E.owner.hasAttribute(key.replace('_', '-')))
        res = E.owner.getAttribute(key.replace('_', '-'));
    else if (zdot_conf[key] && !zdot_conf[key].match(/^{\[=.*\]}$/))
        res = zdot_conf[key];
    return res || (res=='' ? def_empty : res);
}

function check_filter(arr, val, is_filter_in){
    arr = typeof arr=='string' ? [arr] : arr;
    if (!arr || !(arr instanceof Array))
        return true;
    return is_filter_in==arr.indexOf(val)>=0;
}

// adding ?hola_provider_force or ?hola_provider_force=disabled to uri
// overwrites register_percent and register_browser config
function is_force_disabled(){
    var v = get_conf('force', 'enabled');
    return v=='disabled';
}

function is_env_disabled(){
    var v;
    if (!(v = get_conf('register_browser')))
        return false;
    var browser = external_util.user_agent.guess_browser();
    var guess = external_util.user_agent.guess();
    var platform = guess.mobile ? 'mobile' : guess.tv ? 'tv' :
        'desktop';
    if (browser.opera && browser.browser=='chrome')
        browser.browser = 'opera';
    try { v = JSON.parse(v); } catch(e){ v = {}; }
    if (!check_filter(v.browser_in, browser.browser, true) ||
        !check_filter(v.browser_out, browser.browser, false) ||
        !check_filter(v.os_in, guess.os, true) ||
        !check_filter(v.os_out, guess.os, false) ||
        !check_filter(v.platform_in, platform, true) ||
        !check_filter(v.platform_out, platform, false))
    {
        return true;
    }
    return false;
}

function is_register_disabled(){
    var v;
    if (v = get_conf('register_percent', 'n/a'))
    {
        if (isNaN(v)||v<0||v>100)
        {
            console.error(E.provider_id+': invalid register_percent, '+
                'expected a value between 0 and 100 but '+v+' found');
            return true;
        }
        else if (!v || Math.random()*100>v)
            return true;
    }
    return false;
}

E.init = function(provider_id){
    E.provider_id = provider_id;
    E.owner = document.currentScript ||
        document.querySelector('#'+E.provider_id);
    var v, init_conf = {autoinit: true, disabled: false, hls_params: {}};
    if (zdot_conf.embedded_provider)
        init_conf.autoinit = false;
    else
    {
        if (get_conf('manual_init', true))
            init_conf.autoinit = false;
        init_conf.disabled = is_register_disabled();
    }
    if (v = get_conf('hls_params'))
    {
        try {
            init_conf.hls_params = external_util.conv.parse_obj(
                JSON.parse(v), {func: true, re: true});
        } catch(e){}
    }
    init_conf.disabled = init_conf.disabled || is_force_disabled() ||
        is_env_disabled();
    return init_conf;
};

module.exports = E;

},{"./external_util.js":2,"./zdot_conf.js":4}],2:[function(_dereq_,module,exports){
// XXX volodymyr: common container for stuff copied from the main project
// for the sake of avoiding dependency
var E = module.exports = {};

// XXX vadiml: copied from pkg/util/conv.js
E.conv = (function(){
var E = {};
function parse_leaf(v, opt){
    if (!v || typeof v!='object' || Object.keys(v).length!=1)
        return v;
    if (v.__Function__ && opt.func)
        return new Function('', '"use strict";return ('+v.__Function__+');')();
    if (v.__RegExp__ && opt.re)
    {
        var parsed = /^\/(.*)\/(\w*)$/.exec(v.__RegExp__);
        if (!parsed)
            throw new Error('failed parsing regexp');
        return new RegExp(parsed[1], parsed[2]);
    }
    return v;
}
function parse_obj(v, opt){
    if (!v || typeof v!='object')
        return v;
    if (Array.isArray(v))
    {
        for (var i = 0; i<v.length; i++)
            v[i] = parse_obj(v[i], opt);
        return v;
    }
    var v2 = parse_leaf(v, opt);
    if (v2!==v)
        return v2;
    for (var key in v)
        v[key] = parse_obj(v[key], opt);
    return v;
}
E.parse_obj = parse_obj;
return E;
})();

// XXX volodymyr: copied from pkg/util/user_agent.js
E.user_agent = (function(){
var E = {};

// XXX arik: sync: util/user_agent.js <-> svc/cdn/pub/play_loader.js
var win_versions = {
    '10.0': '10.0',
    '6.3': '8.1',
    '6.2': '8',
    '6.1': '7',
    '6.0': 'vista',
    '5.2': '2003',
    '5.1': 'xp',
    '5.0': '2000',
};

var arch_mapping = {
    x86_64: '64',
    i686: '32',
    arm: 'arm',
};

var check_hola = /\bhola_android\b/i;
var check_android_cdn = /Android.* CDNService\/([0-9\.]+)$/;
var check_ios_cdn = / CDNService\/([0-9\.]+)$/;
var check_opera = /\bOPR\b\/(\d+)/i;
var check_edge = /\bEdge\b\/(\d+)/i;
var check_xbox = /\bxbox\b/i;
var check_ucbrowser = /\bUCBrowser\b\/(\d+)/i;
var check_webview = / Version\/(\d+)(\.\d)/;

// var is_browser = !is_node_ff && typeof window!='undefined';
var is_browser = true;

function ios_ua(ua, safari_ver){
    var res;
    if (res = /(?:iPhone|iPad|iPod|iPod touch);.*?OS ([\d._]+)/.exec(ua))
    {
        var ios_ver = res[1];
        return {browser: 'safari', version: safari_ver||ios_ver, ios: ios_ver,
            hola_ios: (is_browser&&window.hola_cdn_sdk)||
            check_ios_cdn.test(ua)};
    }
    if (/HolaCDN iOS/.exec(ua))
        return {browser: 'safari', hola_ios: true};
}

function check_chromium(ua){
    if (check_webview.test(ua) || /Android/.test(ua) || / Mobile /.test(ua) ||
        check_opera.test(ua))
    {
        return false;
    }
    var group = '(?: (\\w*)\\/)?', ver = '\\/[\\d\\.]+';
    var res = new RegExp('AppleWebKit'+ver+'(?: \\(.*\\))?'+group+'.* Chrome'+
        ver+group+'.* Safari'+ver+group).exec(ua);
    return res && (res[1]||res[2]||res[3]);
}

E.guess_browser = function(ua){
    var res;
    ua = ua || (is_browser ? window.navigator&&navigator.userAgent : '');
    if (res = /\bOpera Mini\/(\d+)/.exec(ua))
        return {browser: 'opera_mini', version: res[1]};
    var ucbrowser = check_ucbrowser.exec(ua);
    if (res = /[( ]MSIE ([6789]|10).\d[);]/.exec(ua))
        return {browser: 'ie', version: res[1], xbox: check_xbox.test(ua)};
    if (res = /[( ]Trident\/\d+(\.\d)+.*rv:(\d\d)(\.\d)+[);]/.exec(ua))
        return {browser: 'ie', version: res[2], xbox: check_xbox.test(ua)};
    if (res = / Chrome\/(\d+)(\.\d+)+.* Safari\/\d+(\.\d+)+/.exec(ua))
    {
        var opera = check_opera.exec(ua);
        var edge;
        if (edge = check_edge.exec(ua))
            return {browser: 'edge', version: edge[1]};
        return {browser: 'chrome', version: res[1],
            android: ua.match(/Android/),
            webview: ua.match(check_webview),
            hola_android: check_hola.test(ua),
            hola_app: check_android_cdn.test(ua),
            chromium_based: check_chromium(ua),
            opera: opera && !!opera[1],
            opera_version: opera ? opera[1] : undefined,
            ucbrowser: ucbrowser && !!ucbrowser[1],
            ucbrowser_version: ucbrowser ? ucbrowser[1] : undefined,
            webos_app: /Web0S/.test(ua)};
    }
    if (res = / QupZilla\/(\d+\.\d+\.\d+).* Safari\/\d+.\d+/.exec(ua))
        return {browser: 'qupzilla', version: res[1]};
    if (res = /\(PlayStation (\d+) (\d+\.\d+)\).* AppleWebKit\/\d+.\d+/
        .exec(ua))
    {
        return {browser: 'playstation'+res[1], version: res[2]};
    }
    if (res = / Version\/(\d+)(\.\d)+.* Safari\/\d+.\d+/.exec(ua))
    {
        if (!ua.match(/Android/))
            return ios_ua(ua, res[1])||{browser: 'safari', version: res[1]};
        return {browser: 'chrome', version: res[1], android: true,
            webview: true, hola_android: check_hola.test(ua),
            hola_app: check_android_cdn.test(ua),
            ucbrowser: ucbrowser && !!ucbrowser[1],
            ucbrowser_version: ucbrowser ? ucbrowser[1] : undefined};
    }
    if (res = / (Firefox|PaleMoon)\/(\d+).\d/.exec(ua))
    {
        return {browser: 'firefox', version: res[2],
            palemoon: res[1]=='PaleMoon'};
    }
    if (/Hola\/\d+\.\d+.*?(?:iPhone|iPad|iPod)/.exec(ua))
        return {browser: 'safari', version: 'Hola'};
    if (res = ios_ua(ua))
        return res;
    return {};
};

E.guess = function(ua, platform){
    var res;
    ua = ua || (is_browser ? navigator.userAgent : '');
    platform = platform || (is_browser ? navigator.platform : '');
    if (check_xbox.exec(ua))
        return {os: 'xbox', mobile: false};
    if (res = /Windows(?: NT(?: (.*?))?)?[);]/.exec(ua))
    {
        return {
            os: 'windows',
            version: win_versions[res[1]],
            release_version: res[1],
            arch: ua.match(/WOW64|Win64|x64/) ? '64' : '32',
            mobile: false,
        };
    }
    if (res = /Macintosh.*; (?:Intel|PPC) Mac OS X (\d+[._]\d+)/.exec(ua))
        return {os: 'macos', version: res[1].replace('_', '.'), mobile: false};
    if (/Macintosh/.exec(ua))
        return {os: 'macos', mobile: false};
    if (/Web0S.*SmartTV/.exec(ua))
    {
        return {os: 'webos', version: /Chrome/.test(ua) ? '3' : '2',
            mobile: false, tv: true};
    }
    if (res = /Android(?: (\d+\.\d+))?/.exec(ua))
        return {os: 'android', version: res[1], mobile: true};
    if (res = /(Linux|CrOS|OpenBSD|FreeBSD)(?: (x86_64|i686|arm))?/.exec(ua))
    {
        if (check_opera.test(ua) && res[1]=='Linux' && res[2]=='x86_64' &&
            /^Linux arm/.test(platform))
        {
            // Opera Mobile bug
            return {os: 'android', mobile: true};
        }
        return {
            os: res[1].toLowerCase(),
            arch: arch_mapping[res[2]],
            nix: true,
            mobile: false,
        };
    }
    if (res = /(?:iPhone|iPad|iPod|iPod touch);.*?OS (\d+[._]\d+)/.exec(ua))
        return {os: 'ios', version: res[1].replace('_', '.'), mobile: true};
    if (/iPhone|iPad|iPod|HolaCDN iOS/.exec(ua))
        return {os: 'ios', mobile: true};
    if (/PLAYSTATION/.exec(ua))
        return {os: 'ps', mobile: false};
    if (/Windows Phone/.exec(ua))
        return {os: 'winphone', mobile: true};
    // HitLeap Viewer (whatever it is) is Windows-only
    if (/HitLeap Viewer/.exec(ua))
        return {os: 'windows', mobile: false};
    return {};
};

return E;
})();

},{}],3:[function(_dereq_,module,exports){
function main(){
    var conf = _dereq_('./conf.js').init('hola_videojs_hls_provider');
    if (conf.disabled)
        return {disabled: true, attach: function(){}, detach: function(){}};
    var Hls = window.Hls = _dereq_('hls.js');
    var provider = _dereq_('@hola.org/videojs5-hlsjs-source-handler');
    provider.Hls = Hls;
    provider.version = '1.1.1';
    provider.provider_version = provider.VERSION;
    provider.hls_version = provider.Hls.version;
    provider.hls_params = conf.hls_params;
    if (conf.autoinit)
        provider.attach();
    return provider;
}

try { module.exports = main(); }
catch(e){ module.exports = {init_failure: e}; }

},{"./conf.js":1,"@hola.org/videojs5-hlsjs-source-handler":5,"hls.js":6}],4:[function(_dereq_,module,exports){
// XXX pavlo: workaround for UglifyJS optimization
var is_set = function(v){ return v==1; };
// XXX volodymyr: change it to zdot
module.exports = {
    embedded_provider: is_set('{[=it.HOLA_EMBEDDED_PROVIDER]}'),
    register_percent: '{[=it.HOLA_REGISTER_PERCENT]}',
    register_browser: '{[=it.HOLA_REGISTER_BROWSER]}',
    hls_params: '{[=it.HOLA_HLS_PARAMS]}',
};

},{}],5:[function(_dereq_,module,exports){
'use strict';
var E = module.exports;
var hlsjsConfig;
var attached = false, disabled = false;

E.VERSION = '1.1.4';
E.name = 'HolaProviderHLS';

E.attach = function(obsolete_param, videojs, Hls, hlsjsConfig_){
    E.videojs = videojs||E.videojs||window.videojs;
    if (!E.videojs)
    {
        // XXX volodymyr: allows adding hola provider before main player
        // script (e.g. for brightcove which may autocreate players on init)
        return Object.defineProperty(window, 'videojs', {
            configurable: true, get: function(){},
            set: function(videojs2){
                Object.defineProperty(window, 'videojs',
                    {configurable: true, value: videojs2});
                E.attach(obsolete_param, videojs2, Hls, hlsjsConfig_);
            },
        });
    }
    E.Hls = Hls||E.Hls||window.Hls;
    if (hlsjsConfig_)
        hlsjsConfig = hlsjsConfig_;
    if (attached)
        disabled = false;
    else if (E.Hls.isSupported())
    {
        attached = true;
        // XXX volodymyr: originally we register hola provider inside Html5
        // tech, but in specific environment (e.g vjs+dm/hlsjs) hls videos
        // may be handled by a separate tech, so depending on options.techOrder
        // Html5 tech may be ignored == our provider not used. so we register
        // our source handler to all hls-based techs, but ideally we should
        // present our own tech instead
        var registered_techs = ['Hlsjs', 'Html5'].filter(function(tech){
            if (!(tech = E.videojs.getTech(tech)))
                return;
            // XXX yurij hack: some customers register their own provider and
            // prevent other from being registered with 0 index (rge related)
            if (tech.sourceHandlers instanceof Array)
                tech.sourceHandlers.splice(0, 0, E);
            else
                tech.registerSourceHandler(E, 0);
            // XXX yurij: prevent others handlers being registered with 0 index
            var r = tech.registerSourceHandler;
            tech.registerSourceHandler = function(e, i){
                return r.call(tech, e, i===0 ? 1 : i); };
            return true;
        });
        E.videojs.HolaProviderHLS = HolaProviderHLS;
        if (registered_techs.length)
        {
            console.log(E.name+' registered as %s SourceHandler',
                registered_techs.join('/'));
        }
        else
            console.log(E.name+' not registered: no suitable tech found');
    }
    else
        console.error('Hls.js is not supported in this browser!');
};

E.detach = function(){
    // we don't unregister source handler, just set it as disabled so it will
    // return false in canHandleSource()
    disabled = true;
};

E.canPlayType = function(type){
    if (disabled)
        return '';
    if (/^application\/x-mpegURL$/i.test(type))
        return 'probably';
    return '';
};

E.canHandleSource = function(source){
    var _can_play_type;
    if (disabled)
        return '';
    if (_can_play_type = E.canPlayType(source.type))
        return _can_play_type;
    if (/\.m3u8/i.test(source.src))
        return 'maybe';
    return '';
};

E.handleSource = function(source, tech){
    if (tech.hlsProvider)
        tech.hlsProvider.dispose();
    return tech.hlsProvider = new HolaProviderHLS(source, tech);
};

function HolaProviderHLS(source, tech){
    console.log('init hola/hls provider v'+E.VERSION+' hls v'+E.Hls.version+
        (E.version ? ' hap v'+E.version : ''));
    tech.name_ = 'holaHLS';
    var _video = tech.el();
    var _hls;
    var _errorCounts = {};
    var _duration = null;
    var _seekableStart = 0;
    var _seekableEnd = 0;
    var _player = _video.player||_video.parentNode.player||
        E.videojs.getPlayers()[_video.playerId];
    var opt = _player.options();
    var _preload = opt.preload!='none' || opt.autoplay;
    _video.addEventListener('error', function(evt){
        var errorTxt, mediaError = evt.currentTarget.error;
        switch(mediaError.code)
        {
        case mediaError.MEDIA_ERR_ABORTED:
            errorTxt = 'You aborted the video playback';
            break;
        case mediaError.MEDIA_ERR_DECODE:
            errorTxt = 'The video playback was aborted due to a corruption problem or because the video used features your browser did not support';
            _handleMediaError(mediaError);
            break;
        case mediaError.MEDIA_ERR_NETWORK:
            errorTxt = 'A network error caused the video download to fail part-way';
            break;
        case mediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
            errorTxt = 'The video could not be loaded, either because the server or network failed or because the format is not supported';
            break;
        }
        console.error('MEDIA_ERROR: ', errorTxt);
    });

    function reset(){
        // XXX volodymyr: Hlsjs tech may have preset dm instance on player init
        if (_player.techName_=='Hlsjs' && tech.hls_)
        {
            tech.hls_.destroy();
            tech.hls_ = null;
        }
        // XXX yurij: byteark uses it's own method to determine duration
        // which relies on tech.hlsHandler.isLive existance for live videos
        if (tech.hlsHandler)
            tech.hlsHandler.dispose();
        tech.hlsHandler = {
            dispose: function(){}, // byteark handleSource expects dispose
            isLive: function(){ return _duration==Infinity; },
        };
    }
    function _log(method, message){
        if (_hls && _hls.holaLog && _hls.holaLog[method])
            _hls.holaLog[method].call(_hls.holaLog, message);
    }
    function initialize(){
        var hola_log, hls_params = {};
        Object.assign(hls_params, hlsjsConfig);
        Object.assign(hls_params, E.hls_params||{});
        if (hls_params.debug!==undefined)
            hola_log = hls_params.debug;
        hls_params.debug = {};
        ['debug', 'info', 'log', 'warn','error'].forEach(function(method){
            hls_params.debug[method] = _log.bind(null, method); });
        tech.hls_obj = _hls = new E.Hls(E.videojs.mergeOptions(
            tech.options_.hlsjsConfig, hls_params));
        _hls.holaLog = hola_log;
        _hls.manual_level = -1;
        _hls.on(E.Hls.Events.ERROR, function(event, data){
            _onError(event, data, tech, _errorCounts);
        });
        _hls.on(E.Hls.Events.LEVEL_SWITCH, updateQuality);
        _hls.on(E.Hls.Events.LEVEL_LOADED, function(event, data){
            _duration = data.details.live ? Infinity :
                data.details.totalduration;
        });
        _hls.on(E.Hls.Events.LEVEL_UPDATED, function(event, data){
            _seekableStart = data.details.live ?
                data.details.fragments[0].start : 0;
            _seekableEnd = data.details.live ?
                _hls.streamController.computeLivePosition(_seekableStart,
                data.details) : data.details.totalduration;
        });
        _hls.on(E.Hls.Events.FRAG_PARSING_METADATA, function(event, data){
            tech.trigger('parsedmetadata', data);
        });
        _hls.attachMedia(_video);
        _video.addEventListener('waiting', _onWaitingForData);
    }

    this.duration = function(){
        return _duration || _video.duration || 0;
    };

    this.seekable = function(){
        return E.videojs.createTimeRanges([[_seekableStart, _seekableEnd]]);
    };

    this.dispose = function(){
        _video.removeEventListener('waiting', _onWaitingForData);
        _hls.destroy();
    };

    function load(source){
        _hls.loadSource(source.src);
    }

    function switchQuality(qualityId){
        _hls.manual_level = qualityId;
        if (_hls.hola_adaptive)
            _player.trigger('mediachange');
        else
            _hls.loadLevel = qualityId;
        updateQuality();
    }

    function _handleMediaError(error){
        if (_errorCounts[E.Hls.ErrorTypes.MEDIA_ERROR] === 1)
        {
            console.info('trying to recover media error');
            _hls.recoverMediaError();
        }
        else if (_errorCounts[E.Hls.ErrorTypes.MEDIA_ERROR] === 2)
        {
            console.info('2nd try to recover media error (by swapping audio codec');
            _hls.swapAudioCodec();
            _hls.recoverMediaError();
        }
        else if (_errorCounts[E.Hls.ErrorTypes.MEDIA_ERROR] > 2)
        {
            console.info('bubbling media error up to VIDEOJS');
            error.code = 3;
            tech.error = function(){ return error; };
            tech.trigger('error');
        }
    }

    function _onError(event, data){
        var level = 'error';
        var error = {
            message: ('HLS.js error: ' + data.type + ' - fatal: ' + data.fatal + ' - ' + data.details),
        };
        if (!data.fatal &&
            data.details === E.Hls.ErrorDetails.BUFFER_STALLED_ERROR)
        {
            level = 'warn';
        }
        console[level](error.message);
        // increment/set error count
        _errorCounts[data.type] ?
            _errorCounts[data.type] += 1 : _errorCounts[data.type] = 1;
        // implement simple error handling based on hls.js documentation
        // (https://github.com/dailymotion/hls.js/blob/master/API.md#fifth-step-error-handling)
        if (data.fatal)
        {
            switch (data.type)
            {
            case E.Hls.ErrorTypes.NETWORK_ERROR:
                console.info('bubbling network error up to VIDEOJS');
                error.code = 2;
                tech.error = function() { return error; };
                tech.trigger('error');
                break;
            case E.Hls.ErrorTypes.MEDIA_ERROR:
                _handleMediaError(error);
                break;
            default:
                // cannot recover
                _hls.destroy();
                console.info('bubbling error up to VIDEOJS');
                tech.error = function() { return error; };
                tech.trigger('error');
                break;
            }
        }
    }

    function scaledNumber(num){
        if (num===undefined)
            return '';
        if (!num)
            return '0';
        var k = 1024;
        var sizes = ['', 'K', 'M', 'G', 'T', 'P'];
        var i = Math.floor(Math.log(num)/Math.log(k));
        num /= Math.pow(k, i);
        if (num<0.001)
            return '0';
        if (num>=k-1)
            num = Math.trunc(num);
        var str = num.toFixed(num<1 ? 3 : num<10 ? 2 : num<100 ? 1 : 0);
        return str.replace(/\.0*$/, '')+sizes[i];
    }

    function heightLabel(level){
        var height = level.height || Math.round(level.width * 9 / 16);
        return height ? height + 'p' : '';
    }

    function bitrateLabel(level){
        return level.bitrate ? scaledNumber(level.bitrate) + 'bps' : '';
    }

    function levelLabel(level, levels){
        var label = heightLabel(level);
        if (!label)
            return bitrateLabel(level);
        var duplicated = levels.some(function(l){
            return l!=level && heightLabel(l)==label;
        });
        return  duplicated ? label+' '+bitrateLabel(level) : label;
    }

    function updateQuality(){
        var list = [], levels = _hls.levels;
        if (levels.length > 1)
            list.push({id: -1, label: 'Auto'});
        levels.forEach(function(level, index){
            list.push({id: index, label: levelLabel(level, levels),
                bitrate: level.bitrate});
        });
        // event can be triggered before player started to listen it
        // so we need to save quality_data somewhere
        var data = tech.quality_data = {
            quality: {
                list: list,
                selected: _hls.manual_level,
                current: _hls.loadLevel,
            },
            callback: switchQuality,
        };
        tech.trigger('loadedqualitydata', data);
    }

    function _onWaitingForData() {
        if (!_preload)
            load(source);
        _video.removeEventListener('waiting', _onWaitingForData);
    }

    reset();
    initialize();
    if (_preload)
        load(source);
}

},{}],6:[function(_dereq_,module,exports){
(function (global){
(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.Hls = 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 _dereq_=="function"&&_dereq_;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 _dereq_=="function"&&_dereq_;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

function EventEmitter() {
  this._events = this._events || {};
  this._maxListeners = this._maxListeners || undefined;
}
module.exports = EventEmitter;

// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;

EventEmitter.prototype._events = undefined;
EventEmitter.prototype._maxListeners = undefined;

// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
EventEmitter.defaultMaxListeners = 10;

// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function(n) {
  if (!isNumber(n) || n < 0 || isNaN(n))
    throw TypeError('n must be a positive number');
  this._maxListeners = n;
  return this;
};

EventEmitter.prototype.emit = function(type) {
  var er, handler, len, args, i, listeners;

  if (!this._events)
    this._events = {};

  // If there is no 'error' event listener then throw.
  if (type === 'error') {
    if (!this._events.error ||
        (isObject(this._events.error) && !this._events.error.length)) {
      er = arguments[1];
      if (er instanceof Error) {
        throw er; // Unhandled 'error' event
      } else {
        // At least give some kind of context to the user
        var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
        err.context = er;
        throw err;
      }
    }
  }

  handler = this._events[type];

  if (isUndefined(handler))
    return false;

  if (isFunction(handler)) {
    switch (arguments.length) {
      // fast cases
      case 1:
        handler.call(this);
        break;
      case 2:
        handler.call(this, arguments[1]);
        break;
      case 3:
        handler.call(this, arguments[1], arguments[2]);
        break;
      // slower
      default:
        args = Array.prototype.slice.call(arguments, 1);
        handler.apply(this, args);
    }
  } else if (isObject(handler)) {
    args = Array.prototype.slice.call(arguments, 1);
    listeners = handler.slice();
    len = listeners.length;
    for (i = 0; i < len; i++)
      listeners[i].apply(this, args);
  }

  return true;
};

EventEmitter.prototype.addListener = function(type, listener) {
  var m;

  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  if (!this._events)
    this._events = {};

  // To avoid recursion in the case that type === "newListener"! Before
  // adding it to the listeners, first emit "newListener".
  if (this._events.newListener)
    this.emit('newListener', type,
              isFunction(listener.listener) ?
              listener.listener : listener);

  if (!this._events[type])
    // Optimize the case of one listener. Don't need the extra array object.
    this._events[type] = listener;
  else if (isObject(this._events[type]))
    // If we've already got an array, just append.
    this._events[type].push(listener);
  else
    // Adding the second element, need to change to array.
    this._events[type] = [this._events[type], listener];

  // Check for listener leak
  if (isObject(this._events[type]) && !this._events[type].warned) {
    if (!isUndefined(this._maxListeners)) {
      m = this._maxListeners;
    } else {
      m = EventEmitter.defaultMaxListeners;
    }

    if (m && m > 0 && this._events[type].length > m) {
      this._events[type].warned = true;
      console.error('(node) warning: possible EventEmitter memory ' +
                    'leak detected. %d listeners added. ' +
                    'Use emitter.setMaxListeners() to increase limit.',
                    this._events[type].length);
      if (typeof console.trace === 'function') {
        // not supported in IE 10
        console.trace();
      }
    }
  }

  return this;
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

EventEmitter.prototype.once = function(type, listener) {
  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  var fired = false;

  function g() {
    this.removeListener(type, g);

    if (!fired) {
      fired = true;
      listener.apply(this, arguments);
    }
  }

  g.listener = listener;
  this.on(type, g);

  return this;
};

// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener = function(type, listener) {
  var list, position, length, i;

  if (!isFunction(listener))
    throw TypeError('listener must be a function');

  if (!this._events || !this._events[type])
    return this;

  list = this._events[type];
  length = list.length;
  position = -1;

  if (list === listener ||
      (isFunction(list.listener) && list.listener === listener)) {
    delete this._events[type];
    if (this._events.removeListener)
      this.emit('removeListener', type, listener);

  } else if (isObject(list)) {
    for (i = length; i-- > 0;) {
      if (list[i] === listener ||
          (list[i].listener && list[i].listener === listener)) {
        position = i;
        break;
      }
    }

    if (position < 0)
      return this;

    if (list.length === 1) {
      list.length = 0;
      delete this._events[type];
    } else {
      list.splice(position, 1);
    }

    if (this._events.removeListener)
      this.emit('removeListener', type, listener);
  }

  return this;
};

EventEmitter.prototype.removeAllListeners = function(type) {
  var key, listeners;

  if (!this._events)
    return this;

  // not listening for removeListener, no need to emit
  if (!this._events.removeListener) {
    if (arguments.length === 0)
      this._events = {};
    else if (this._events[type])
      delete this._events[type];
    return this;
  }

  // emit removeListener for all listeners on all events
  if (arguments.length === 0) {
    for (key in this._events) {
      if (key === 'removeListener') continue;
      this.removeAllListeners(key);
    }
    this.removeAllListeners('removeListener');
    this._events = {};
    return this;
  }

  listeners = this._events[type];

  if (isFunction(listeners)) {
    this.removeListener(type, listeners);
  } else if (listeners) {
    // LIFO order
    while (listeners.length)
      this.removeListener(type, listeners[listeners.length - 1]);
  }
  delete this._events[type];

  return this;
};

EventEmitter.prototype.listeners = function(type) {
  var ret;
  if (!this._events || !this._events[type])
    ret = [];
  else if (isFunction(this._events[type]))
    ret = [this._events[type]];
  else
    ret = this._events[type].slice();
  return ret;
};

EventEmitter.prototype.listenerCount = function(type) {
  if (this._events) {
    var evlistener = this._events[type];

    if (isFunction(evlistener))
      return 1;
    else if (evlistener)
      return evlistener.length;
  }
  return 0;
};

EventEmitter.listenerCount = function(emitter, type) {
  return emitter.listenerCount(type);
};

function isFunction(arg) {
  return typeof arg === 'function';
}

function isNumber(arg) {
  return typeof arg === 'number';
}

function isObject(arg) {
  return typeof arg === 'object' && arg !== null;
}

function isUndefined(arg) {
  return arg === void 0;
}

},{}],2:[function(_dereq_,module,exports){
// see https://tools.ietf.org/html/rfc1808

/* jshint ignore:start */
(function(root) { 
/* jshint ignore:end */

  var URL_REGEX = /^((?:[^\/;?#]+:)?)(\/\/[^\/\;?#]*)?(.*?)??(;.*?)?(\?.*?)?(#.*?)?$/;
  var FIRST_SEGMENT_REGEX = /^([^\/;?#]*)(.*)$/;
  var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g;
  var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g;

  var URLToolkit = { // jshint ignore:line
    // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or //
    // E.g
    // With opts.alwaysNormalize = false (default, spec compliant)
    // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g
    // With opts.alwaysNormalize = true (default, not spec compliant)
    // http://a.com/b/cd + /e/f/../g => http://a.com/e/g
    buildAbsoluteURL: function(baseURL, relativeURL, opts) {
      opts = opts || {};
      // remove any remaining space and CRLF
      baseURL = baseURL.trim();
      relativeURL = relativeURL.trim();
      if (!relativeURL) {
        // 2a) If the embedded URL is entirely empty, it inherits the
        // entire base URL (i.e., is set equal to the base URL)
        // and we are done.
        if (!opts.alwaysNormalize) {
          return baseURL;
        }
        var basePartsForNormalise = this.parseURL(baseURL);
        if (!baseParts) {
          throw new Error('Error trying to parse base URL.');
        }
        basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path);
        return URLToolkit.buildURLFromParts(basePartsForNormalise);
      }
      var relativeParts = this.parseURL(relativeURL);
      if (!relativeParts) {
        throw new Error('Error trying to parse relative URL.');
      }
      if (relativeParts.scheme) {
        // 2b) If the embedded URL starts with a scheme name, it is
        // interpreted as an absolute URL and we are done.
        if (!opts.alwaysNormalize) {
          return relativeURL;
        }
        relativeParts.path = URLToolkit.normalizePath(relativeParts.path);
        return URLToolkit.buildURLFromParts(relativeParts);
      }
      var baseParts = this.parseURL(baseURL);
      if (!baseParts) {
        throw new Error('Error trying to parse base URL.');
      }
      if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {
        // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc
        // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a'
        var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);
        baseParts.netLoc = pathParts[1];
        baseParts.path = pathParts[2];
      }
      if (baseParts.netLoc && !baseParts.path) {
        baseParts.path = '/';
      }
      var builtParts = {
        // 2c) Otherwise, the embedded URL inherits the scheme of
        // the base URL.
        scheme: baseParts.scheme,
        netLoc: relativeParts.netLoc,
        path: null,
        params: relativeParts.params,
        query: relativeParts.query,
        fragment: relativeParts.fragment
      };
      if (!relativeParts.netLoc) {
        // 3) If the embedded URL's <net_loc> is non-empty, we skip to
        // Step 7.  Otherwise, the embedded URL inherits the <net_loc>
        // (if any) of the base URL.
        builtParts.netLoc = baseParts.netLoc;
        // 4) If the embedded URL path is preceded by a slash "/", the
        // path is not relative and we skip to Step 7.
        if (relativeParts.path[0] !== '/') {
          if (!relativeParts.path) {
            // 5) If the embedded URL path is empty (and not preceded by a
            // slash), then the embedded URL inherits the base URL path
            builtParts.path = baseParts.path;
            // 5a) if the embedded URL's <params> is non-empty, we skip to
            // step 7; otherwise, it inherits the <params> of the base
            // URL (if any) and
            if (!relativeParts.params) {
              builtParts.params = baseParts.params;
              // 5b) if the embedded URL's <query> is non-empty, we skip to
              // step 7; otherwise, it inherits the <query> of the base
              // URL (if any) and we skip to step 7.
              if (!relativeParts.query) {
                builtParts.query = baseParts.query;
              }
            }
          } else {
            // 6) The last segment of the base URL's path (anything
            // following the rightmost slash "/", or the entire path if no
            // slash is present) is removed and the embedded URL's path is
            // appended in its place.
            var baseURLPath = baseParts.path;
            var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path;
            builtParts.path = URLToolkit.normalizePath(newPath);
          }
        }
      }
      if (builtParts.path === null) {
        builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path;
      }
      return URLToolkit.buildURLFromParts(builtParts);
    },
    parseURL: function(url) {
      var parts = URL_REGEX.exec(url);
      if (!parts) {
        return null;
      }
      return {
        scheme: parts[1] || '',
        netLoc: parts[2] || '',
        path: parts[3] || '',
        params: parts[4] || '',
        query: parts[5] || '',
        fragment: parts[6] || ''
      };
    },
    normalizePath: function(path) {
      // The following operations are
      // then applied, in order, to the new path:
      // 6a) All occurrences of "./", where "." is a complete path
      // segment, are removed.
      // 6b) If the path ends with "." as a complete path segment,
      // that "." is removed.
      path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, '');
      // 6c) All occurrences of "<segment>/../", where <segment> is a
      // complete path segment not equal to "..", are removed.
      // Removal of these path segments is performed iteratively,
      // removing the leftmost matching pattern on each iteration,
      // until no matching pattern remains.
      // 6d) If the path ends with "<segment>/..", where <segment> is a
      // complete path segment not equal to "..", that
      // "<segment>/.." is removed.
      while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length) {} // jshint ignore:line
      return path.split('').reverse().join('');
    },
    buildURLFromParts: function(parts) {
      return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment;
    }
  };

/* jshint ignore:start */
  if(typeof exports === 'object' && typeof module === 'object')
    module.exports = URLToolkit;
  else if(typeof define === 'function' && define.amd)
    define([], function() { return URLToolkit; });
  else if(typeof exports === 'object')
    exports["URLToolkit"] = URLToolkit;
  else
    root["URLToolkit"] = URLToolkit;
})(this);
/* jshint ignore:end */

},{}],3:[function(_dereq_,module,exports){
var bundleFn = arguments[3];
var sources = arguments[4];
var cache = arguments[5];

var stringify = JSON.stringify;

module.exports = function (fn, options) {
    var wkey;
    var cacheKeys = Object.keys(cache);

    for (var i = 0, l = cacheKeys.length; i < l; i++) {
        var key = cacheKeys[i];
        var exp = cache[key].exports;
        // Using babel as a transpiler to use esmodule, the export will always
        // be an object with the default export as a property of it. To ensure
        // the existing api and babel esmodule exports are both supported we
        // check for both
        if (exp === fn || exp && exp.default === fn) {
            wkey = key;
            break;
        }
    }

    if (!wkey) {
        wkey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
        var wcache = {};
        for (var i = 0, l = cacheKeys.length; i < l; i++) {
            var key = cacheKeys[i];
            wcache[key] = key;
        }
        sources[wkey] = [
            Function(['require','module','exports'], '(' + fn + ')(self)'),
            wcache
        ];
    }
    var skey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);

    var scache = {}; scache[wkey] = wkey;
    sources[skey] = [
        Function(['require'], (
            // try to call default if defined to also support babel esmodule
            // exports
            'var f = require(' + stringify(wkey) + ');' +
            '(f.default ? f.default : f)(self);'
        )),
        scache
    ];

    var workerSources = {};
    resolveSources(skey);

    function resolveSources(key) {
        workerSources[key] = true;

        for (var depPath in sources[key][1]) {
            var depKey = sources[key][1][depPath];
            if (!workerSources[depKey]) {
                resolveSources(depKey);
            }
        }
    }

    var src = '(' + bundleFn + ')({'
        + Object.keys(workerSources).map(function (key) {
            return stringify(key) + ':['
                + sources[key][0]
                + ',' + stringify(sources[key][1]) + ']'
            ;
        }).join(',')
        + '},{},[' + stringify(skey) + '])'
    ;

    var URL = window.URL || window.webkitURL || window.mozURL || window.msURL;

    var blob = new Blob([src], { type: 'text/javascript' });
    if (options && options.bare) { return blob; }
    var workerUrl = URL.createObjectURL(blob);
    var worker = new Worker(workerUrl);
    worker.objectURL = workerUrl;
    return worker;
};

},{}],4:[function(_dereq_,module,exports){
/**
 * HLS config
 */
'use strict';

Object.defineProperty(exports, "__esModule", {
      value: true
});
exports.hlsDefaultConfig = undefined;

var _abrController = _dereq_(5);

var _abrController2 = _interopRequireDefault(_abrController);

var _bufferController = _dereq_(8);

var _bufferController2 = _interopRequireDefault(_bufferController);

var _capLevelController = _dereq_(9);

var _capLevelController2 = _interopRequireDefault(_capLevelController);

var _fpsController = _dereq_(10);

var _fpsController2 = _interopRequireDefault(_fpsController);

var _xhrLoader = _dereq_(59);

var _xhrLoader2 = _interopRequireDefault(_xhrLoader);

var _audioTrackController = _dereq_(7);

var _audioTrackController2 = _interopRequireDefault(_audioTrackController);

var _audioStreamController = _dereq_(6);

var _audioStreamController2 = _interopRequireDefault(_audioStreamController);

var _cues = _dereq_(50);

var _cues2 = _interopRequireDefault(_cues);

var _timelineController = _dereq_(16);

var _timelineController2 = _interopRequireDefault(_timelineController);

var _subtitleTrackController = _dereq_(15);

var _subtitleTrackController2 = _interopRequireDefault(_subtitleTrackController);

var _subtitleStreamController = _dereq_(14);

var _subtitleStreamController2 = _interopRequireDefault(_subtitleStreamController);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

//#endif

//#endif

//#if subtitle

//import FetchLoader from './utils/fetch-loader';
//#if altaudio
var hlsDefaultConfig = exports.hlsDefaultConfig = {
      autoStartLoad: true, // used by stream-controller
      startPosition: -1, // used by stream-controller
      defaultAudioCodec: undefined, // used by stream-controller
      debug: false, // used by logger
      capLevelOnFPSDrop: false, // used by fps-controller
      capLevelToPlayerSize: false, // used by cap-level-controller
      initialLiveManifestSize: 1, // used by stream-controller
      maxBufferLength: 30, // used by stream-controller
      maxBufferSize: 60 * 1000 * 1000, // used by stream-controller
      maxBufferHole: 0.5, // used by stream-controller
      maxSeekHole: 2, // used by stream-controller
      lowBufferWatchdogPeriod: 0.5, // used by stream-controller
      highBufferWatchdogPeriod: 3, // used by stream-controller
      nudgeOffset: 0.1, // used by stream-controller
      nudgeMaxRetry: 3, // used by stream-controller
      maxFragLookUpTolerance: 0.25, // used by stream-controller
      liveSyncDurationCount: 3, // used by stream-controller
      liveMaxLatencyDurationCount: Infinity, // used by stream-controller
      liveSyncDuration: undefined, // used by stream-controller
      liveMaxLatencyDuration: undefined, // used by stream-controller
      maxMaxBufferLength: 600, // used by stream-controller
      enableWorker: true, // used by demuxer
      enableSoftwareAES: true, // used by decrypter
      manifestLoadingTimeOut: 10000, // used by playlist-loader
      manifestLoadingMaxRetry: 1, // used by playlist-loader
      manifestLoadingRetryDelay: 1000, // used by playlist-loader
      manifestLoadingMaxRetryTimeout: 64000, // used by playlist-loader
      startLevel: undefined, // used by level-controller
      levelLoadingTimeOut: 10000, // used by playlist-loader
      levelLoadingMaxRetry: 4, // used by playlist-loader
      levelLoadingRetryDelay: 1000, // used by playlist-loader
      levelLoadingMaxRetryTimeout: 64000, // used by playlist-loader
      fragLoadingTimeOut: 20000, // used by fragment-loader
      fragLoadingMaxRetry: 6, // used by fragment-loader
      fragLoadingRetryDelay: 1000, // used by fragment-loader
      fragLoadingMaxRetryTimeout: 64000, // used by fragment-loader
      fragLoadingLoopThreshold: 3, // used by stream-controller
      startFragPrefetch: false, // used by stream-controller
      fpsDroppedMonitoringPeriod: 5000, // used by fps-controller
      fpsDroppedMonitoringThreshold: 0.2, // used by fps-controller
      appendErrorMaxRetry: 3, // used by buffer-controller
      loader: _xhrLoader2.default,
      //loader: FetchLoader,
      fLoader: undefined,
      pLoader: undefined,
      xhrSetup: undefined,
      fetchSetup: undefined,
      abrController: _abrController2.default,
      bufferController: _bufferController2.default,
      capLevelController: _capLevelController2.default,
      fpsController: _fpsController2.default,
      //#if altaudio
      audioStreamController: _audioStreamController2.default,
      audioTrackController: _audioTrackController2.default,
      //#endif
      //#if subtitle
      subtitleStreamController: _subtitleStreamController2.default,
      subtitleTrackController: _subtitleTrackController2.default,
      timelineController: _timelineController2.default,
      cueHandler: _cues2.default,
      enableCEA708Captions: true, // used by timeline-controller
      enableWebVTT: true, // used by timeline-controller
      captionsTextTrack1Label: 'English', // used by timeline-controller
      captionsTextTrack1LanguageCode: 'en', // used by timeline-controller
      captionsTextTrack2Label: 'Spanish', // used by timeline-controller
      captionsTextTrack2LanguageCode: 'es', // used by timeline-controller
      //#endif
      stretchShortVideoTrack: false, // used by mp4-remuxer
      forceKeyFrameOnDiscontinuity: true, // used by ts-demuxer
      abrEwmaFastLive: 3, // used by abr-controller
      abrEwmaSlowLive: 9, // used by abr-controller
      abrEwmaFastVoD: 3, // used by abr-controller
      abrEwmaSlowVoD: 9, // used by abr-controller
      abrEwmaDefaultEstimate: 5e5, // 500 kbps  // used by abr-controller
      abrBandWidthFactor: 0.95, // used by abr-controller
      abrBandWidthUpFactor: 0.7, // used by abr-controller
      abrMaxWithRealBitrate: false, // used by abr-controller
      maxStarvationDelay: 4, // used by abr-controller
      maxLoadingDelay: 4, // used by abr-controller
      minAutoBitrate: 0 // used by hls
};

},{"10":10,"14":14,"15":15,"16":16,"5":5,"50":50,"59":59,"6":6,"7":7,"8":8,"9":9}],5:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _bufferHelper = _dereq_(37);

var _bufferHelper2 = _interopRequireDefault(_bufferHelper);

var _errors = _dereq_(33);

var _logger = _dereq_(54);

var _ewmaBandwidthEstimator = _dereq_(52);

var _ewmaBandwidthEstimator2 = _interopRequireDefault(_ewmaBandwidthEstimator);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * simple ABR Controller
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                *  - compute next level based on last fragment bw heuristics
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                *  - implement an abandon rules triggered if we have less than 2 frag buffered and if computed bw shows that we risk buffer stalling
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                */

var AbrController = function (_EventHandler) {
  _inherits(AbrController, _EventHandler);

  function AbrController(hls) {
    _classCallCheck(this, AbrController);

    var _this = _possibleConstructorReturn(this, (AbrController.__proto__ || Object.getPrototypeOf(AbrController)).call(this, hls, _events2.default.FRAG_LOADING, _events2.default.FRAG_LOADED, _events2.default.FRAG_BUFFERED, _events2.default.ERROR));

    _this.lastLoadedFragLevel = 0;
    _this._nextAutoLevel = -1;
    _this.hls = hls;
    _this.timer = null;
    _this._bwEstimator = null;
    _this.onCheck = _this._abandonRulesCheck.bind(_this);
    return _this;
  }

  _createClass(AbrController, [{
    key: 'destroy',
    value: function destroy() {
      this.clearTimer();
      _eventHandler2.default.prototype.destroy.call(this);
    }
  }, {
    key: 'onFragLoading',
    value: function onFragLoading(data) {
      var frag = data.frag;
      if (frag.type === 'main') {
        if (!this.timer) {
          this.timer = setInterval(this.onCheck, 100);
        }
        // lazy init of bw Estimator, rationale is that we use different params for Live/VoD
        // so we need to wait for stream manifest / playlist type to instantiate it.
        if (!this._bwEstimator) {
          var hls = this.hls,
              level = data.frag.level,
              isLive = hls.levels[level].details.live,
              config = hls.config,
              ewmaFast = void 0,
              ewmaSlow = void 0;

          if (isLive) {
            ewmaFast = config.abrEwmaFastLive;
            ewmaSlow = config.abrEwmaSlowLive;
          } else {
            ewmaFast = config.abrEwmaFastVoD;
            ewmaSlow = config.abrEwmaSlowVoD;
          }
          this._bwEstimator = new _ewmaBandwidthEstimator2.default(hls, ewmaSlow, ewmaFast, config.abrEwmaDefaultEstimate);
        }
        this.fragCurrent = frag;
      }
    }
  }, {
    key: '_abandonRulesCheck',
    value: function _abandonRulesCheck() {
      /*
        monitor fragment retrieval time...
        we compute expected time of arrival of the complete fragment.
        we compare it to expected time of buffer starvation
      */
      var hls = this.hls,
          v = hls.media,
          frag = this.fragCurrent,
          loader = frag.loader,
          minAutoLevel = hls.minAutoLevel;

      // if loader has been destroyed or loading has been aborted, stop timer and return
      if (!loader || loader.stats && loader.stats.aborted) {
        _logger.logger.warn('frag loader destroy or aborted, disarm abandonRules');
        this.clearTimer();
        return;
      }
      var stats = loader.stats;
      /* only monitor frag retrieval time if
      (video not paused OR first fragment being loaded(ready state === HAVE_NOTHING = 0)) AND autoswitching enabled AND not lowest level (=> means that we have several levels) */
      if (v && stats && (!v.paused && v.playbackRate !== 0 || !v.readyState) && frag.autoLevel && frag.level) {
        var requestDelay = performance.now() - stats.trequest,
            playbackRate = Math.abs(v.playbackRate);
        // monitor fragment load progress after half of expected fragment duration,to stabilize bitrate
        if (requestDelay > 500 * frag.duration / playbackRate) {
          var levels = hls.levels,
              loadRate = Math.max(1, stats.bw ? stats.bw / 8 : stats.loaded * 1000 / requestDelay),
              // byte/s; at least 1 byte/s to avoid division by zero
          // compute expected fragment length using frag duration and level bitrate. also ensure that expected len is gte than already loaded size
          level = levels[frag.level],
              levelBitrate = level.realBitrate ? Math.max(level.realBitrate, level.bitrate) : level.bitrate,
              expectedLen = stats.total ? stats.total : Math.max(stats.loaded, Math.round(frag.duration * levelBitrate / 8)),
              pos = v.currentTime,
              fragLoadedDelay = (expectedLen - stats.loaded) / loadRate,
              bufferStarvationDelay = (_bufferHelper2.default.bufferInfo(v, pos, hls.config.maxBufferHole).end - pos) / playbackRate;
          // consider emergency switch down only if we have less than 2 frag buffered AND
          // time to finish loading current fragment is bigger than buffer starvation delay
          // ie if we risk buffer starvation if bw does not increase quickly
          if (bufferStarvationDelay < 2 * frag.duration / playbackRate && fragLoadedDelay > bufferStarvationDelay) {
            var fragLevelNextLoadedDelay = void 0,
                nextLoadLevel = void 0;
            // lets iterate through lower level and try to find the biggest one that could avoid rebuffering
            // we start from current level - 1 and we step down , until we find a matching level
            for (nextLoadLevel = frag.level - 1; nextLoadLevel > minAutoLevel; nextLoadLevel--) {
              // compute time to load next fragment at lower level
              // 0.8 : consider only 80% of current bw to be conservative
              // 8 = bits per byte (bps/Bps)
              var levelNextBitrate = levels[nextLoadLevel].realBitrate ? Math.max(levels[nextLoadLevel].realBitrate, levels[nextLoadLevel].bitrate) : levels[nextLoadLevel].bitrate;
              fragLevelNextLoadedDelay = frag.duration * levelNextBitrate / (8 * 0.8 * loadRate);
              if (fragLevelNextLoadedDelay < bufferStarvationDelay) {
                // we found a lower level that be rebuffering free with current estimated bw !
                break;
              }
            }
            // only emergency switch down if it takes less time to load new fragment at lowest level instead
            // of finishing loading current one ...
            if (fragLevelNextLoadedDelay < fragLoadedDelay) {
              _logger.logger.warn('loading too slow, abort fragment loading and switch to level ' + nextLoadLevel + ':fragLoadedDelay[' + nextLoadLevel + ']<fragLoadedDelay[' + (frag.level - 1) + '];bufferStarvationDelay:' + fragLevelNextLoadedDelay.toFixed(1) + '<' + fragLoadedDelay.toFixed(1) + ':' + bufferStarvationDelay.toFixed(1));
              // force next load level in auto mode
              hls.nextLoadLevel = nextLoadLevel;
              // update bw estimate for this fragment before cancelling load (this will help reducing the bw)
              this._bwEstimator.sample(requestDelay, stats.loaded);
              //abort fragment loading
              loader.abort();
              // stop abandon rules timer
              this.clearTimer();
              hls.trigger(_events2.default.FRAG_LOAD_EMERGENCY_ABORTED, { frag: frag, stats: stats });
            }
          }
        }
      }
    }
  }, {
    key: 'onFragLoaded',
    value: function onFragLoaded(data) {
      var frag = data.frag;
      if (frag.type === 'main' && !isNaN(frag.sn)) {
        // stop monitoring bw once frag loaded
        this.clearTimer();
        // store level id after successful fragment load
        this.lastLoadedFragLevel = frag.level;
        // reset forced auto level value so that next level will be selected
        this._nextAutoLevel = -1;

        // compute level average bitrate
        if (this.hls.config.abrMaxWithRealBitrate) {
          var level = this.hls.levels[frag.level];
          var loadedBytes = (level.loaded ? level.loaded.bytes : 0) + data.stats.loaded;
          var loadedDuration = (level.loaded ? level.loaded.duration : 0) + data.frag.duration;
          level.loaded = { bytes: loadedBytes, duration: loadedDuration };
          level.realBitrate = Math.round(8 * loadedBytes / loadedDuration);
        }
        // if fragment has been loaded to perform a bitrate test,
        if (data.frag.bitrateTest) {
          var stats = data.stats;
          stats.tparsed = stats.tbuffered = stats.tload;
          this.onFragBuffered(data);
        }
      }
    }
  }, {
    key: 'onFragBuffered',
    value: function onFragBuffered(data) {
      var stats = data.stats,
          frag = data.frag;
      // only update stats on first frag buffering
      // if same frag is loaded multiple times, it might be in browser cache, and loaded quickly
      // and leading to wrong bw estimation
      // on bitrate test, also only update stats once (if tload = tbuffered == on FRAG_LOADED)
      if (stats.aborted !== true && frag.loadCounter === 1 && frag.type === 'main' && !isNaN(frag.sn) && (!frag.bitrateTest || stats.tload === stats.tbuffered)) {
        // use tparsed-trequest instead of tbuffered-trequest to compute fragLoadingProcessing; rationale is that  buffer appending only happens once media is attached
        // in case we use config.startFragPrefetch while media is not attached yet, fragment might be parsed while media not attached yet, but it will only be buffered on media attached
        // as a consequence it could happen really late in the process. meaning that appending duration might appears huge ... leading to underestimated throughput estimation
        var fragLoadingProcessingMs = stats.tparsed - stats.trequest;
        _logger.logger.log('latency/loading/parsing/append/kbps:' + Math.round(stats.tfirst - stats.trequest) + '/' + Math.round(stats.tload - stats.tfirst) + '/' + Math.round(stats.tparsed - stats.tload) + '/' + Math.round(stats.tbuffered - stats.tparsed) + '/' + Math.round(8 * stats.loaded / (stats.tbuffered - stats.trequest)));
        this._bwEstimator.sample(fragLoadingProcessingMs, stats.loaded);
        stats.bwEstimate = this._bwEstimator.getEstimate();
        // if fragment has been loaded to perform a bitrate test, (hls.startLevel = -1), store bitrate test delay duration
        if (frag.bitrateTest) {
          this.bitrateTestDelay = fragLoadingProcessingMs / 1000;
        } else {
          this.bitrateTestDelay = 0;
        }
      }
    }
  }, {
    key: 'onError',
    value: function onError(data) {
      // stop timer in case of frag loading error
      switch (data.details) {
        case _errors.ErrorDetails.FRAG_LOAD_ERROR:
        case _errors.ErrorDetails.FRAG_LOAD_TIMEOUT:
          this.clearTimer();
          break;
        default:
          break;
      }
    }
  }, {
    key: 'clearTimer',
    value: function clearTimer() {
      clearInterval(this.timer);
      this.timer = null;
    }

    // return next auto level

  }, {
    key: '_findBestLevel',
    value: function _findBestLevel(currentLevel, currentFragDuration, currentBw, minAutoLevel, maxAutoLevel, maxFetchDuration, bwFactor, bwUpFactor, levels) {
      for (var i = maxAutoLevel; i >= minAutoLevel; i--) {
        var levelInfo = levels[i],
            levelDetails = levelInfo.details,
            avgDuration = levelDetails ? levelDetails.totalduration / levelDetails.fragments.length : currentFragDuration,
            live = levelDetails ? levelDetails.live : false,
            adjustedbw = void 0;
        // follow algorithm captured from stagefright :
        // https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp
        // Pick the highest bandwidth stream below or equal to estimated bandwidth.
        // consider only 80% of the available bandwidth, but if we are switching up,
        // be even more conservative (70%) to avoid overestimating and immediately
        // switching back.
        if (i <= currentLevel) {
          adjustedbw = bwFactor * currentBw;
        } else {
          adjustedbw = bwUpFactor * currentBw;
        }
        var bitrate = levels[i].realBitrate ? Math.max(levels[i].realBitrate, levels[i].bitrate) : levels[i].bitrate,
            fetchDuration = bitrate * avgDuration / adjustedbw;

        _logger.logger.trace('level/adjustedbw/bitrate/avgDuration/maxFetchDuration/fetchDuration: ' + i + '/' + Math.round(adjustedbw) + '/' + bitrate + '/' + avgDuration + '/' + maxFetchDuration + '/' + fetchDuration);
        // if adjusted bw is greater than level bitrate AND
        if (adjustedbw > bitrate && (
        // fragment fetchDuration unknown OR live stream OR fragment fetchDuration less than max allowed fetch duration, then this level matches
        // we don't account for max Fetch Duration for live streams, this is to avoid switching down when near the edge of live sliding window ...
        // special case to support startLevel = -1 (bitrateTest) on live streams : in that case we should not exit loop so that _findBestLevel will return -1
        !fetchDuration || live && !this.bitrateTestDelay || fetchDuration < maxFetchDuration)) {
          // as we are looping from highest to lowest, this will return the best achievable quality level
          return i;
        }
      }
      // not enough time budget even with quality level 0 ... rebuffering might happen
      return -1;
    }
  }, {
    key: 'nextAutoLevel',
    get: function get() {
      var forcedAutoLevel = this._nextAutoLevel;
      var bwEstimator = this._bwEstimator;
      // in case next auto level has been forced, and bw not available or not reliable, return forced value
      if (forcedAutoLevel !== -1 && (!bwEstimator || !bwEstimator.canEstimate())) {
        return forcedAutoLevel;
      }
      // compute next level using ABR logic
      var nextABRAutoLevel = this._nextABRAutoLevel;
      // if forced auto level has been defined, use it to cap ABR computed quality level
      if (forcedAutoLevel !== -1) {
        nextABRAutoLevel = Math.min(forcedAutoLevel, nextABRAutoLevel);
      }
      return nextABRAutoLevel;
    },
    set: function set(nextLevel) {
      this._nextAutoLevel = nextLevel;
    }
  }, {
    key: '_nextABRAutoLevel',
    get: function get() {
      var hls = this.hls,
          maxAutoLevel = hls.maxAutoLevel,
          levels = hls.levels,
          config = hls.config,
          minAutoLevel = hls.minAutoLevel;
      var v = hls.media,
          currentLevel = this.lastLoadedFragLevel,
          currentFragDuration = this.fragCurrent ? this.fragCurrent.duration : 0,
          pos = v ? v.currentTime : 0,

      // playbackRate is the absolute value of the playback rate; if v.playbackRate is 0, we use 1 to load as
      // if we're playing back at the normal rate.
      playbackRate = v && v.playbackRate !== 0 ? Math.abs(v.playbackRate) : 1.0,
          avgbw = this._bwEstimator ? this._bwEstimator.getEstimate() : config.abrEwmaDefaultEstimate,

      // bufferStarvationDelay is the wall-clock time left until the playback buffer is exhausted.
      bufferStarvationDelay = (_bufferHelper2.default.bufferInfo(v, pos, config.maxBufferHole).end - pos) / playbackRate;

      // First, look to see if we can find a level matching with our avg bandwidth AND that could also guarantee no rebuffering at all
      var bestLevel = this._findBestLevel(currentLevel, currentFragDuration, avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, config.abrBandWidthFactor, config.abrBandWidthUpFactor, levels);
      if (bestLevel >= 0) {
        return bestLevel;
      } else {
        _logger.logger.trace('rebuffering expected to happen, lets try to find a quality level minimizing the rebuffering');
        // not possible to get rid of rebuffering ... let's try to find level that will guarantee less than maxStarvationDelay of rebuffering
        // if no matching level found, logic will return 0
        var maxStarvationDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxStarvationDelay) : config.maxStarvationDelay,
            bwFactor = config.abrBandWidthFactor,
            bwUpFactor = config.abrBandWidthUpFactor;
        if (bufferStarvationDelay === 0) {
          // in case buffer is empty, let's check if previous fragment was loaded to perform a bitrate test
          var bitrateTestDelay = this.bitrateTestDelay;
          if (bitrateTestDelay) {
            // if it is the case, then we need to adjust our max starvation delay using maxLoadingDelay config value
            // max video loading delay used in  automatic start level selection :
            // in that mode ABR controller will ensure that video loading time (ie the time to fetch the first fragment at lowest quality level +
            // the time to fetch the fragment at the appropriate quality level is less than ```maxLoadingDelay``` )
            // cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration
            var maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay;
            maxStarvationDelay = maxLoadingDelay - bitrateTestDelay;
            _logger.logger.trace('bitrate test took ' + Math.round(1000 * bitrateTestDelay) + 'ms, set first fragment max fetchDuration to ' + Math.round(1000 * maxStarvationDelay) + ' ms');
            // don't use conservative factor on bitrate test
            bwFactor = bwUpFactor = 1;
          }
        }
        bestLevel = this._findBestLevel(currentLevel, currentFragDuration, avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay + maxStarvationDelay, bwFactor, bwUpFactor, levels);
        return Math.max(bestLevel, 0);
      }
    }
  }]);

  return AbrController;
}(_eventHandler2.default);

exports.default = AbrController;

},{"33":33,"34":34,"35":35,"37":37,"52":52,"54":54}],6:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _binarySearch = _dereq_(48);

var _binarySearch2 = _interopRequireDefault(_binarySearch);

var _bufferHelper = _dereq_(37);

var _bufferHelper2 = _interopRequireDefault(_bufferHelper);

var _demuxer = _dereq_(25);

var _demuxer2 = _interopRequireDefault(_demuxer);

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _levelHelper = _dereq_(38);

var _levelHelper2 = _interopRequireDefault(_levelHelper);

var _timeRanges = _dereq_(55);

var _timeRanges2 = _interopRequireDefault(_timeRanges);

var _errors = _dereq_(33);

var _logger = _dereq_(54);

var _discontinuities = _dereq_(51);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * Audio Stream Controller
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               */

var State = {
  STOPPED: 'STOPPED',
  STARTING: 'STARTING',
  IDLE: 'IDLE',
  PAUSED: 'PAUSED',
  KEY_LOADING: 'KEY_LOADING',
  FRAG_LOADING: 'FRAG_LOADING',
  FRAG_LOADING_WAITING_RETRY: 'FRAG_LOADING_WAITING_RETRY',
  WAITING_TRACK: 'WAITING_TRACK',
  PARSING: 'PARSING',
  PARSED: 'PARSED',
  BUFFER_FLUSHING: 'BUFFER_FLUSHING',
  ENDED: 'ENDED',
  ERROR: 'ERROR',
  WAITING_INIT_PTS: 'WAITING_INIT_PTS'
};

var AudioStreamController = function (_EventHandler) {
  _inherits(AudioStreamController, _EventHandler);

  function AudioStreamController(hls) {
    _classCallCheck(this, AudioStreamController);

    var _this = _possibleConstructorReturn(this, (AudioStreamController.__proto__ || Object.getPrototypeOf(AudioStreamController)).call(this, hls, _events2.default.MEDIA_ATTACHED, _events2.default.MEDIA_DETACHING, _events2.default.AUDIO_TRACKS_UPDATED, _events2.default.AUDIO_TRACK_SWITCHING, _events2.default.AUDIO_TRACK_LOADED, _events2.default.KEY_LOADED, _events2.default.FRAG_LOADED, _events2.default.FRAG_PARSING_INIT_SEGMENT, _events2.default.FRAG_PARSING_DATA, _events2.default.FRAG_PARSED, _events2.default.ERROR, _events2.default.BUFFER_CREATED, _events2.default.BUFFER_APPENDED, _events2.default.BUFFER_FLUSHED, _events2.default.INIT_PTS_FOUND));

    _this.config = hls.config;
    _this.audioCodecSwap = false;
    _this.ticks = 0;
    _this._state = State.STOPPED;
    _this.ontick = _this.tick.bind(_this);
    _this.initPTS = [];
    _this.waitingFragment = null;
    _this.videoTrackCC = null;
    return _this;
  }

  _createClass(AudioStreamController, [{
    key: 'destroy',
    value: function destroy() {
      this.stopLoad();
      if (this.timer) {
        clearInterval(this.timer);
        this.timer = null;
      }
      _eventHandler2.default.prototype.destroy.call(this);
      this.state = State.STOPPED;
    }

    //Signal that video PTS was found

  }, {
    key: 'onInitPtsFound',
    value: function onInitPtsFound(data) {
      var demuxerId = data.id,
          cc = data.frag.cc,
          initPTS = data.initPTS;
      if (demuxerId === 'main') {
        //Always update the new INIT PTS
        //Can change due level switch
        this.initPTS[cc] = initPTS;
        this.videoTrackCC = cc;
        _logger.logger.log('InitPTS for cc:' + cc + ' found from video track:' + initPTS);

        //If we are waiting we need to demux/remux the waiting frag
        //With the new initPTS
        if (this.state === State.WAITING_INIT_PTS) {
          this.tick();
        }
      }
    }
  }, {
    key: 'startLoad',
    value: function startLoad(startPosition) {
      if (this.tracks) {
        var lastCurrentTime = this.lastCurrentTime;
        this.stopLoad();
        if (!this.timer) {
          this.timer = setInterval(this.ontick, 100);
        }
        this.fragLoadError = 0;
        if (lastCurrentTime > 0 && startPosition === -1) {
          _logger.logger.log('audio:override startPosition with lastCurrentTime @' + lastCurrentTime.toFixed(3));
          this.state = State.IDLE;
        } else {
          this.lastCurrentTime = this.startPosition ? this.startPosition : startPosition;
          this.state = State.STARTING;
        }
        this.nextLoadPosition = this.startPosition = this.lastCurrentTime;
        this.tick();
      } else {
        this.startPosition = startPosition;
        this.state = State.STOPPED;
      }
    }
  }, {
    key: 'stopLoad',
    value: function stopLoad() {
      var frag = this.fragCurrent;
      if (frag) {
        if (frag.loader) {
          frag.loader.abort();
        }
        this.fragCurrent = null;
      }
      this.fragPrevious = null;
      if (this.demuxer) {
        this.demuxer.destroy();
        this.demuxer = null;
      }
      this.state = State.STOPPED;
    }
  }, {
    key: 'tick',
    value: function tick() {
      this.ticks++;
      if (this.ticks === 1) {
        this.doTick();
        if (this.ticks > 1) {
          setTimeout(this.tick, 1);
        }
        this.ticks = 0;
      }
    }
  }, {
    key: 'doTick',
    value: function doTick() {
      var pos,
          track,
          trackDetails,
          hls = this.hls,
          config = hls.config;
      //logger.log('audioStream:' + this.state);
      switch (this.state) {
        case State.ERROR:
        //don't do anything in error state to avoid breaking further ...
        case State.PAUSED:
        //don't do anything in paused state either ...
        case State.BUFFER_FLUSHING:
          break;
        case State.STARTING:
          this.state = State.WAITING_TRACK;
          this.loadedmetadata = false;
          break;
        case State.IDLE:
          var tracks = this.tracks;
          // audio tracks not received => exit loop
          if (!tracks) {
            break;
          }
          // if video not attached AND
          // start fragment already requested OR start frag prefetch disable
          // exit loop
          // => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop
          if (!this.media && (this.startFragRequested || !config.startFragPrefetch)) {
            break;
          }
          // determine next candidate fragment to be loaded, based on current position and
          //  end of buffer position
          // if we have not yet loaded any fragment, start loading from start position
          if (this.loadedmetadata) {
            pos = this.media.currentTime;
          } else {
            pos = this.nextLoadPosition;
            if (pos === undefined) {
              break;
            }
          }
          var media = this.mediaBuffer ? this.mediaBuffer : this.media,
              bufferInfo = _bufferHelper2.default.bufferInfo(media, pos, config.maxBufferHole),
              bufferLen = bufferInfo.len,
              bufferEnd = bufferInfo.end,
              fragPrevious = this.fragPrevious,
              maxBufLen = config.maxMaxBufferLength,
              audioSwitch = this.audioSwitch,
              trackId = this.trackId;

          // if buffer length is less than maxBufLen try to load a new fragment
          if ((bufferLen < maxBufLen || audioSwitch) && trackId < tracks.length) {
            trackDetails = tracks[trackId].details;
            // if track info not retrieved yet, switch state and wait for track retrieval
            if (typeof trackDetails === 'undefined') {
              this.state = State.WAITING_TRACK;
              break;
            }

            // we just got done loading the final fragment, check if we need to finalize media stream
            if (!audioSwitch && !trackDetails.live && fragPrevious && fragPrevious.sn === trackDetails.endSN) {
              // if we are not seeking or if we are seeking but everything (almost) til the end is buffered, let's signal eos
              // we don't compare exactly media.duration === bufferInfo.end as there could be some subtle media duration difference when switching
              // between different renditions. using half frag duration should help cope with these cases.
              if (!this.media.seeking || this.media.duration - bufferEnd < fragPrevious.duration / 2) {
                // Finalize the media stream
                this.hls.trigger(_events2.default.BUFFER_EOS, { type: 'audio' });
                this.state = State.ENDED;
                break;
              }
            }

            // find fragment index, contiguous with end of buffer position
            var fragments = trackDetails.fragments,
                fragLen = fragments.length,
                start = fragments[0].start,
                end = fragments[fragLen - 1].start + fragments[fragLen - 1].duration,
                frag = void 0;

            // When switching audio track, reload audio as close as possible to currentTime
            if (audioSwitch) {
              if (trackDetails.live && !trackDetails.PTSKnown) {
                _logger.logger.log('switching audiotrack, live stream, unknown PTS,load first fragment');
                bufferEnd = 0;
              } else {
                bufferEnd = pos;
                // if currentTime (pos) is less than alt audio playlist start time, it means that alt audio is ahead of currentTime
                if (trackDetails.PTSKnown && pos < start) {
                  // if everything is buffered from pos to start or if audio buffer upfront, let's seek to start
                  if (bufferInfo.end > start || bufferInfo.nextStart) {
                    _logger.logger.log('alt audio track ahead of main track, seek to start of alt audio track');
                    this.media.currentTime = start + 0.05;
                  } else {
                    return;
                  }
                }
              }
            }
            if (trackDetails.initSegment && !trackDetails.initSegment.data) {
              frag = trackDetails.initSegment;
            }
            // if bufferEnd before start of playlist, load first fragment
            else if (bufferEnd <= start) {
                frag = fragments[0];
                if (this.videoTrackCC !== null && frag.cc !== this.videoTrackCC) {
                  // Ensure we find a fragment which matches the continuity of the video track
                  frag = (0, _discontinuities.findFragWithCC)(fragments, this.videoTrackCC);
                }
                if (trackDetails.live && frag.loadIdx && frag.loadIdx === this.fragLoadIdx) {
                  // we just loaded this first fragment, and we are still lagging behind the start of the live playlist
                  // let's force seek to start
                  var nextBuffered = bufferInfo.nextStart ? bufferInfo.nextStart : start;
                  _logger.logger.log('no alt audio available @currentTime:' + this.media.currentTime + ', seeking @' + (nextBuffered + 0.05));
                  this.media.currentTime = nextBuffered + 0.05;
                  return;
                }
              } else {
                var foundFrag = void 0;
                var maxFragLookUpTolerance = config.maxFragLookUpTolerance;
                var fragNext = fragPrevious ? fragments[fragPrevious.sn - fragments[0].sn + 1] : undefined;
                var fragmentWithinToleranceTest = function fragmentWithinToleranceTest(candidate) {
                  // offset should be within fragment boundary - config.maxFragLookUpTolerance
                  // this is to cope with situations like
                  // bufferEnd = 9.991
                  // frag[Ø] : [0,10]
                  // frag[1] : [10,20]
                  // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here
                  //              frag start               frag start+duration
                  //                  |-----------------------------|
                  //              <--->                         <--->
                  //  ...--------><-----------------------------><---------....
                  // previous frag         matching fragment         next frag
                  //  return -1             return 0                 return 1
                  //logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`);
                  // Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments
                  var candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration);
                  if (candidate.start + candidate.duration - candidateLookupTolerance <= bufferEnd) {
                    return 1;
                  } // if maxFragLookUpTolerance will have negative value then don't return -1 for first element
                  else if (candidate.start - candidateLookupTolerance > bufferEnd && candidate.start) {
                      return -1;
                    }
                  return 0;
                };

                if (bufferEnd < end) {
                  if (bufferEnd > end - maxFragLookUpTolerance) {
                    maxFragLookUpTolerance = 0;
                  }
                  // Prefer the next fragment if it's within tolerance
                  if (fragNext && !fragmentWithinToleranceTest(fragNext)) {
                    foundFrag = fragNext;
                  } else {
                    foundFrag = _binarySearch2.default.search(fragments, fragmentWithinToleranceTest);
                  }
                } else {
                  // reach end of playlist
                  foundFrag = fragments[fragLen - 1];
                }
                if (foundFrag) {
                  frag = foundFrag;
                  start = foundFrag.start;
                  //logger.log('find SN matching with pos:' +  bufferEnd + ':' + frag.sn);
                  if (fragPrevious && frag.level === fragPrevious.level && frag.sn === fragPrevious.sn) {
                    if (frag.sn < trackDetails.endSN) {
                      frag = fragments[frag.sn + 1 - trackDetails.startSN];
                      _logger.logger.log('SN just loaded, load next one: ' + frag.sn);
                    } else {
                      frag = null;
                    }
                  }
                }
              }
            if (frag) {
              //logger.log('      loading frag ' + i +',pos/bufEnd:' + pos.toFixed(3) + '/' + bufferEnd.toFixed(3));
              if (frag.decryptdata && frag.decryptdata.uri != null && frag.decryptdata.key == null) {
                _logger.logger.log('Loading key for ' + frag.sn + ' of [' + trackDetails.startSN + ' ,' + trackDetails.endSN + '],track ' + trackId);
                this.state = State.KEY_LOADING;
                hls.trigger(_events2.default.KEY_LOADING, { frag: frag });
              } else {
                _logger.logger.log('Loading ' + frag.sn + ', cc: ' + frag.cc + ' of [' + trackDetails.startSN + ' ,' + trackDetails.endSN + '],track ' + trackId + ', currentTime:' + pos + ',bufferEnd:' + bufferEnd.toFixed(3));
                // ensure that we are not reloading the same fragments in loop ...
                if (this.fragLoadIdx !== undefined) {
                  this.fragLoadIdx++;
                } else {
                  this.fragLoadIdx = 0;
                }
                if (frag.loadCounter) {
                  frag.loadCounter++;
                  var maxThreshold = config.fragLoadingLoopThreshold;
                  // if this frag has already been loaded 3 times, and if it has been reloaded recently
                  if (frag.loadCounter > maxThreshold && Math.abs(this.fragLoadIdx - frag.loadIdx) < maxThreshold) {
                    hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.FRAG_LOOP_LOADING_ERROR, fatal: false, frag: frag });
                    return;
                  }
                } else {
                  frag.loadCounter = 1;
                }
                frag.loadIdx = this.fragLoadIdx;
                this.fragCurrent = frag;
                this.startFragRequested = true;
                if (!isNaN(frag.sn)) {
                  this.nextLoadPosition = frag.start + frag.duration;
                }
                hls.trigger(_events2.default.FRAG_LOADING, { frag: frag });
                this.state = State.FRAG_LOADING;
              }
            }
          }
          break;
        case State.WAITING_TRACK:
          track = this.tracks[this.trackId];
          // check if playlist is already loaded
          if (track && track.details) {
            this.state = State.IDLE;
          }
          break;
        case State.FRAG_LOADING_WAITING_RETRY:
          var now = performance.now();
          var retryDate = this.retryDate;
          media = this.media;
          var isSeeking = media && media.seeking;
          // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading
          if (!retryDate || now >= retryDate || isSeeking) {
            _logger.logger.log('audioStreamController: retryDate reached, switch back to IDLE state');
            this.state = State.IDLE;
          }
          break;
        case State.WAITING_INIT_PTS:
          var videoTrackCC = this.videoTrackCC;
          if (this.initPTS[videoTrackCC] === undefined) {
            break;
          }

          // Ensure we don't get stuck in the WAITING_INIT_PTS state if the waiting frag CC doesn't match any initPTS
          var waitingFrag = this.waitingFragment;
          if (waitingFrag) {
            var waitingFragCC = waitingFrag.frag.cc;
            if (videoTrackCC !== waitingFragCC) {
              track = this.tracks[this.trackId];
              if (track.details && track.details.live) {
                _logger.logger.warn('Waiting fragment CC (' + waitingFragCC + ') does not match video track CC (' + videoTrackCC + ')');
                this.waitingFragment = null;
                this.state = State.IDLE;
              }
            } else {
              this.state = State.FRAG_LOADING;
              this.onFragLoaded(this.waitingFragment);
              this.waitingFragment = null;
            }
          } else {
            this.state = State.IDLE;
          }

          break;
        case State.STOPPED:
        case State.FRAG_LOADING:
        case State.PARSING:
        case State.PARSED:
        case State.ENDED:
          break;
        default:
          break;
      }
    }
  }, {
    key: 'onMediaAttached',
    value: function onMediaAttached(data) {
      var media = this.media = this.mediaBuffer = data.media;
      this.onvseeking = this.onMediaSeeking.bind(this);
      this.onvended = this.onMediaEnded.bind(this);
      media.addEventListener('seeking', this.onvseeking);
      media.addEventListener('ended', this.onvended);
      var config = this.config;
      if (this.tracks && config.autoStartLoad) {
        this.startLoad(config.startPosition);
      }
    }
  }, {
    key: 'onMediaDetaching',
    value: function onMediaDetaching() {
      var media = this.media;
      if (media && media.ended) {
        _logger.logger.log('MSE detaching and video ended, reset startPosition');
        this.startPosition = this.lastCurrentTime = 0;
      }

      // reset fragment loading counter on MSE detaching to avoid reporting FRAG_LOOP_LOADING_ERROR after error recovery
      var tracks = this.tracks;
      if (tracks) {
        // reset fragment load counter
        tracks.forEach(function (track) {
          if (track.details) {
            track.details.fragments.forEach(function (fragment) {
              fragment.loadCounter = undefined;
            });
          }
        });
      }
      // remove video listeners
      if (media) {
        media.removeEventListener('seeking', this.onvseeking);
        media.removeEventListener('ended', this.onvended);
        this.onvseeking = this.onvseeked = this.onvended = null;
      }
      this.media = this.mediaBuffer = null;
      this.loadedmetadata = false;
      this.stopLoad();
    }
  }, {
    key: 'onMediaSeeking',
    value: function onMediaSeeking() {
      if (this.state === State.ENDED) {
        // switch to IDLE state to check for potential new fragment
        this.state = State.IDLE;
      }
      if (this.media) {
        this.lastCurrentTime = this.media.currentTime;
      }
      // avoid reporting fragment loop loading error in case user is seeking several times on same position
      if (this.fragLoadIdx !== undefined) {
        this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold;
      }
      // tick to speed up processing
      this.tick();
    }
  }, {
    key: 'onMediaEnded',
    value: function onMediaEnded() {
      // reset startPosition and lastCurrentTime to restart playback @ stream beginning
      this.startPosition = this.lastCurrentTime = 0;
    }
  }, {
    key: 'onAudioTracksUpdated',
    value: function onAudioTracksUpdated(data) {
      _logger.logger.log('audio tracks updated');
      this.tracks = data.audioTracks;
    }
  }, {
    key: 'onAudioTrackSwitching',
    value: function onAudioTrackSwitching(data) {
      // if any URL found on new audio track, it is an alternate audio track
      var altAudio = !!data.url;
      this.trackId = data.id;

      this.fragCurrent = null;
      this.state = State.PAUSED;
      this.waitingFragment = null;
      // destroy useless demuxer when switching audio to main
      if (!altAudio) {
        if (this.demuxer) {
          this.demuxer.destroy();
          this.demuxer = null;
        }
      } else {
        // switching to audio track, start timer if not already started
        if (!this.timer) {
          this.timer = setInterval(this.ontick, 100);
        }
      }

      //should we switch tracks ?
      if (altAudio) {
        this.audioSwitch = true;
        //main audio track are handled by stream-controller, just do something if switching to alt audio track
        this.state = State.IDLE;
        // increase fragment load Index to avoid frag loop loading error after buffer flush
        if (this.fragLoadIdx !== undefined) {
          this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold;
        }
      }
      this.tick();
    }
  }, {
    key: 'onAudioTrackLoaded',
    value: function onAudioTrackLoaded(data) {
      var newDetails = data.details,
          trackId = data.id,
          track = this.tracks[trackId],
          duration = newDetails.totalduration,
          sliding = 0;

      _logger.logger.log('track ' + trackId + ' loaded [' + newDetails.startSN + ',' + newDetails.endSN + '],duration:' + duration);

      if (newDetails.live) {
        var curDetails = track.details;
        if (curDetails && newDetails.fragments.length > 0) {
          // we already have details for that level, merge them
          _levelHelper2.default.mergeDetails(curDetails, newDetails);
          sliding = newDetails.fragments[0].start;
          // TODO
          //this.liveSyncPosition = this.computeLivePosition(sliding, curDetails);
          if (newDetails.PTSKnown) {
            _logger.logger.log('live audio playlist sliding:' + sliding.toFixed(3));
          } else {
            _logger.logger.log('live audio playlist - outdated PTS, unknown sliding');
          }
        } else {
          newDetails.PTSKnown = false;
          _logger.logger.log('live audio playlist - first load, unknown sliding');
        }
      } else {
        newDetails.PTSKnown = false;
      }
      track.details = newDetails;

      // compute start position
      if (!this.startFragRequested) {
        // compute start position if set to -1. use it straight away if value is defined
        if (this.startPosition === -1) {
          // first, check if start time offset has been set in playlist, if yes, use this value
          var startTimeOffset = newDetails.startTimeOffset;
          if (!isNaN(startTimeOffset)) {
            _logger.logger.log('start time offset found in playlist, adjust startPosition to ' + startTimeOffset);
            this.startPosition = startTimeOffset;
          } else {
            this.startPosition = 0;
          }
        }
        this.nextLoadPosition = this.startPosition;
      }
      // only switch batck to IDLE state if we were waiting for track to start downloading a new fragment
      if (this.state === State.WAITING_TRACK) {
        this.state = State.IDLE;
      }
      //trigger handler right now
      this.tick();
    }
  }, {
    key: 'onKeyLoaded',
    value: function onKeyLoaded() {
      if (this.state === State.KEY_LOADING) {
        this.state = State.IDLE;
        this.tick();
      }
    }
  }, {
    key: 'onFragLoaded',
    value: function onFragLoaded(data) {
      var fragCurrent = this.fragCurrent,
          fragLoaded = data.frag;
      if (this.state === State.FRAG_LOADING && fragCurrent && fragLoaded.type === 'audio' && fragLoaded.level === fragCurrent.level && fragLoaded.sn === fragCurrent.sn) {
        var track = this.tracks[this.trackId],
            details = track.details,
            duration = details.totalduration,
            trackId = fragCurrent.level,
            sn = fragCurrent.sn,
            cc = fragCurrent.cc,
            audioCodec = this.config.defaultAudioCodec || track.audioCodec || 'mp4a.40.2',
            stats = this.stats = data.stats;
        if (sn === 'initSegment') {
          this.state = State.IDLE;

          stats.tparsed = stats.tbuffered = performance.now();
          details.initSegment.data = data.payload;
          this.hls.trigger(_events2.default.FRAG_BUFFERED, { stats: stats, frag: fragCurrent, id: 'audio' });
          this.tick();
        } else {
          this.state = State.PARSING;
          // transmux the MPEG-TS data to ISO-BMFF segments
          this.appended = false;
          if (!this.demuxer) {
            this.demuxer = new _demuxer2.default(this.hls, 'audio');
          }
          //Check if we have video initPTS
          // If not we need to wait for it
          var initPTS = this.initPTS[cc];
          var initSegmentData = details.initSegment ? details.initSegment.data : [];
          if (details.initSegment || initPTS !== undefined) {
            this.pendingBuffering = true;
            _logger.logger.log('Demuxing ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],track ' + trackId);
            // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live)
            var accurateTimeOffset = false; //details.PTSKnown || !details.live;
            this.demuxer.push(data.payload, initSegmentData, audioCodec, null, fragCurrent, duration, accurateTimeOffset, initPTS);
          } else {
            _logger.logger.log('unknown video PTS for continuity counter ' + cc + ', waiting for video PTS before demuxing audio frag ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],track ' + trackId);
            this.waitingFragment = data;
            this.state = State.WAITING_INIT_PTS;
          }
        }
      }
      this.fragLoadError = 0;
    }
  }, {
    key: 'onFragParsingInitSegment',
    value: function onFragParsingInitSegment(data) {
      var fragCurrent = this.fragCurrent;
      var fragNew = data.frag;
      if (fragCurrent && data.id === 'audio' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === State.PARSING) {
        var tracks = data.tracks,
            track = void 0;

        // delete any video track found on audio demuxer
        if (tracks.video) {
          delete tracks.video;
        }

        // include levelCodec in audio and video tracks
        track = tracks.audio;
        if (track) {
          track.levelCodec = track.codec;
          track.id = data.id;
          this.hls.trigger(_events2.default.BUFFER_CODECS, tracks);
          _logger.logger.log('audio track:audio,container:' + track.container + ',codecs[level/parsed]=[' + track.levelCodec + '/' + track.codec + ']');
          var initSegment = track.initSegment;
          if (initSegment) {
            var appendObj = { type: 'audio', data: initSegment, parent: 'audio', content: 'initSegment' };
            if (this.audioSwitch) {
              this.pendingData = [appendObj];
            } else {
              this.appended = true;
              // arm pending Buffering flag before appending a segment
              this.pendingBuffering = true;
              this.hls.trigger(_events2.default.BUFFER_APPENDING, appendObj);
            }
          }
          //trigger handler right now
          this.tick();
        }
      }
    }
  }, {
    key: 'onFragParsingData',
    value: function onFragParsingData(data) {
      var _this2 = this;

      var fragCurrent = this.fragCurrent;
      var fragNew = data.frag;
      if (fragCurrent && data.id === 'audio' && data.type === 'audio' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === State.PARSING) {
        var trackId = this.trackId,
            track = this.tracks[trackId],
            hls = this.hls;

        if (isNaN(data.endPTS)) {
          data.endPTS = data.startPTS + fragCurrent.duration;
          data.endDTS = data.startDTS + fragCurrent.duration;
        }

        _logger.logger.log('parsed ' + data.type + ',PTS:[' + data.startPTS.toFixed(3) + ',' + data.endPTS.toFixed(3) + '],DTS:[' + data.startDTS.toFixed(3) + '/' + data.endDTS.toFixed(3) + '],nb:' + data.nb);
        _levelHelper2.default.updateFragPTSDTS(track.details, fragCurrent, data.startPTS, data.endPTS);

        var audioSwitch = this.audioSwitch,
            media = this.media,
            appendOnBufferFlush = false;
        //Only flush audio from old audio tracks when PTS is known on new audio track
        if (audioSwitch && media) {
          if (media.readyState) {
            var currentTime = media.currentTime;
            _logger.logger.log('switching audio track : currentTime:' + currentTime);
            if (currentTime >= data.startPTS) {
              _logger.logger.log('switching audio track : flushing all audio');
              this.state = State.BUFFER_FLUSHING;
              hls.trigger(_events2.default.BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY, type: 'audio' });
              appendOnBufferFlush = true;
              //Lets announce that the initial audio track switch flush occur
              this.audioSwitch = false;
              hls.trigger(_events2.default.AUDIO_TRACK_SWITCHED, { id: trackId });
            }
          } else {
            //Lets announce that the initial audio track switch flush occur
            this.audioSwitch = false;
            hls.trigger(_events2.default.AUDIO_TRACK_SWITCHED, { id: trackId });
          }
        }

        var pendingData = this.pendingData;
        if (!this.audioSwitch) {
          [data.data1, data.data2].forEach(function (buffer) {
            if (buffer && buffer.length) {
              pendingData.push({ type: data.type, data: buffer, parent: 'audio', content: 'data' });
            }
          });
          if (!appendOnBufferFlush && pendingData.length) {
            pendingData.forEach(function (appendObj) {
              // only append in PARSING state (rationale is that an appending error could happen synchronously on first segment appending)
              // in that case it is useless to append following segments
              if (_this2.state === State.PARSING) {
                // arm pending Buffering flag before appending a segment
                _this2.pendingBuffering = true;
                _this2.hls.trigger(_events2.default.BUFFER_APPENDING, appendObj);
              }
            });
            this.pendingData = [];
            this.appended = true;
          }
        }
        //trigger handler right now
        this.tick();
      }
    }
  }, {
    key: 'onFragParsed',
    value: function onFragParsed(data) {
      var fragCurrent = this.fragCurrent;
      var fragNew = data.frag;
      if (fragCurrent && data.id === 'audio' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === State.PARSING) {
        this.stats.tparsed = performance.now();
        this.state = State.PARSED;
        this._checkAppendedParsed();
      }
    }
  }, {
    key: 'onBufferCreated',
    value: function onBufferCreated(data) {
      var audioTrack = data.tracks.audio;
      if (audioTrack) {
        this.mediaBuffer = audioTrack.buffer;
        this.loadedmetadata = true;
      }
    }
  }, {
    key: 'onBufferAppended',
    value: function onBufferAppended(data) {
      if (data.parent === 'audio') {
        var state = this.state;
        if (state === State.PARSING || state === State.PARSED) {
          // check if all buffers have been appended
          this.pendingBuffering = data.pending > 0;
          this._checkAppendedParsed();
        }
      }
    }
  }, {
    key: '_checkAppendedParsed',
    value: function _checkAppendedParsed() {
      //trigger handler right now
      if (this.state === State.PARSED && (!this.appended || !this.pendingBuffering)) {
        var frag = this.fragCurrent,
            stats = this.stats,
            hls = this.hls;
        if (frag) {
          this.fragPrevious = frag;
          stats.tbuffered = performance.now();
          hls.trigger(_events2.default.FRAG_BUFFERED, { stats: stats, frag: frag, id: 'audio' });
          var media = this.mediaBuffer ? this.mediaBuffer : this.media;
          _logger.logger.log('audio buffered : ' + _timeRanges2.default.toString(media.buffered));
          if (this.audioSwitch && this.appended) {
            this.audioSwitch = false;
            hls.trigger(_events2.default.AUDIO_TRACK_SWITCHED, { id: this.trackId });
          }
          this.state = State.IDLE;
        }
        this.tick();
      }
    }
  }, {
    key: 'onError',
    value: function onError(data) {
      var frag = data.frag;
      // don't handle frag error not related to audio fragment
      if (frag && frag.type !== 'audio') {
        return;
      }
      switch (data.details) {
        case _errors.ErrorDetails.FRAG_LOAD_ERROR:
        case _errors.ErrorDetails.FRAG_LOAD_TIMEOUT:
          if (!data.fatal) {
            var loadError = this.fragLoadError;
            if (loadError) {
              loadError++;
            } else {
              loadError = 1;
            }
            var config = this.config;
            if (loadError <= config.fragLoadingMaxRetry) {
              this.fragLoadError = loadError;
              // reset load counter to avoid frag loop loading error
              frag.loadCounter = 0;
              // exponential backoff capped to config.fragLoadingMaxRetryTimeout
              var delay = Math.min(Math.pow(2, loadError - 1) * config.fragLoadingRetryDelay, config.fragLoadingMaxRetryTimeout);
              _logger.logger.warn('audioStreamController: frag loading failed, retry in ' + delay + ' ms');
              this.retryDate = performance.now() + delay;
              // retry loading state
              this.state = State.FRAG_LOADING_WAITING_RETRY;
            } else {
              _logger.logger.error('audioStreamController: ' + data.details + ' reaches max retry, redispatch as fatal ...');
              // switch error to fatal
              data.fatal = true;
              this.state = State.ERROR;
            }
          }
          break;
        case _errors.ErrorDetails.FRAG_LOOP_LOADING_ERROR:
        case _errors.ErrorDetails.AUDIO_TRACK_LOAD_ERROR:
        case _errors.ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT:
        case _errors.ErrorDetails.KEY_LOAD_ERROR:
        case _errors.ErrorDetails.KEY_LOAD_TIMEOUT:
          //  when in ERROR state, don't switch back to IDLE state in case a non-fatal error is received
          if (this.state !== State.ERROR) {
            // if fatal error, stop processing, otherwise move to IDLE to retry loading
            this.state = data.fatal ? State.ERROR : State.IDLE;
            _logger.logger.warn('audioStreamController: ' + data.details + ' while loading frag,switch to ' + this.state + ' state ...');
          }
          break;
        case _errors.ErrorDetails.BUFFER_FULL_ERROR:
          // if in appending state
          if (data.parent === 'audio' && (this.state === State.PARSING || this.state === State.PARSED)) {
            var media = this.mediaBuffer,
                currentTime = this.media.currentTime,
                mediaBuffered = media && _bufferHelper2.default.isBuffered(media, currentTime) && _bufferHelper2.default.isBuffered(media, currentTime + 0.5);
            // reduce max buf len if current position is buffered
            if (mediaBuffered) {
              var _config = this.config;
              if (_config.maxMaxBufferLength >= _config.maxBufferLength) {
                // reduce max buffer length as it might be too high. we do this to avoid loop flushing ...
                _config.maxMaxBufferLength /= 2;
                _logger.logger.warn('audio:reduce max buffer length to ' + _config.maxMaxBufferLength + 's');
                // increase fragment load Index to avoid frag loop loading error after buffer flush
                this.fragLoadIdx += 2 * _config.fragLoadingLoopThreshold;
              }
              this.state = State.IDLE;
            } else {
              // current position is not buffered, but browser is still complaining about buffer full error
              // this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708
              // in that case flush the whole audio buffer to recover
              _logger.logger.warn('buffer full error also media.currentTime is not buffered, flush audio buffer');
              this.fragCurrent = null;
              // flush everything
              this.state = State.BUFFER_FLUSHING;
              this.hls.trigger(_events2.default.BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY, type: 'audio' });
            }
          }
          break;
        default:
          break;
      }
    }
  }, {
    key: 'onBufferFlushed',
    value: function onBufferFlushed() {
      var _this3 = this;

      var pendingData = this.pendingData;
      if (pendingData && pendingData.length) {
        _logger.logger.log('appending pending audio data on Buffer Flushed');
        pendingData.forEach(function (appendObj) {
          _this3.hls.trigger(_events2.default.BUFFER_APPENDING, appendObj);
        });
        this.appended = true;
        this.pendingData = [];
        this.state = State.PARSED;
      } else {
        // move to IDLE once flush complete. this should trigger new fragment loading
        this.state = State.IDLE;
        // reset reference to frag
        this.fragPrevious = null;
        this.tick();
      }
    }
  }, {
    key: 'state',
    set: function set(nextState) {
      if (this.state !== nextState) {
        var previousState = this.state;
        this._state = nextState;
        _logger.logger.log('audio stream:' + previousState + '->' + nextState);
      }
    },
    get: function get() {
      return this._state;
    }
  }]);

  return AudioStreamController;
}(_eventHandler2.default);

exports.default = AudioStreamController;

},{"25":25,"33":33,"34":34,"35":35,"37":37,"38":38,"48":48,"51":51,"54":54,"55":55}],7:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _logger = _dereq_(54);

var _errors = _dereq_(33);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * audio track controller
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               */

var AudioTrackController = function (_EventHandler) {
  _inherits(AudioTrackController, _EventHandler);

  function AudioTrackController(hls) {
    _classCallCheck(this, AudioTrackController);

    var _this = _possibleConstructorReturn(this, (AudioTrackController.__proto__ || Object.getPrototypeOf(AudioTrackController)).call(this, hls, _events2.default.MANIFEST_LOADING, _events2.default.MANIFEST_LOADED, _events2.default.AUDIO_TRACK_LOADED, _events2.default.ERROR));

    _this.ticks = 0;
    _this.ontick = _this.tick.bind(_this);
    return _this;
  }

  _createClass(AudioTrackController, [{
    key: 'destroy',
    value: function destroy() {
      this.cleanTimer();
      _eventHandler2.default.prototype.destroy.call(this);
    }
  }, {
    key: 'cleanTimer',
    value: function cleanTimer() {
      if (this.timer) {
        clearTimeout(this.timer);
        this.timer = null;
      }
    }
  }, {
    key: 'tick',
    value: function tick() {
      this.ticks++;
      if (this.ticks === 1) {
        this.doTick();
        if (this.ticks > 1) {
          setTimeout(this.tick, 1);
        }
        this.ticks = 0;
      }
    }
  }, {
    key: 'doTick',
    value: function doTick() {
      this.updateTrack(this.trackId);
    }
  }, {
    key: 'onError',
    value: function onError(data) {
      if (data.fatal && data.type === _errors.ErrorTypes.NETWORK_ERROR) {
        this.cleanTimer();
      }
    }
  }, {
    key: 'onManifestLoading',
    value: function onManifestLoading() {
      // reset audio tracks on manifest loading
      this.tracks = [];
      this.trackId = -1;
    }
  }, {
    key: 'onManifestLoaded',
    value: function onManifestLoaded(data) {
      var _this2 = this;

      var tracks = data.audioTracks || [];
      var defaultFound = false;
      this.tracks = tracks;
      this.hls.trigger(_events2.default.AUDIO_TRACKS_UPDATED, { audioTracks: tracks });
      // loop through available audio tracks and autoselect default if needed
      var id = 0;
      tracks.forEach(function (track) {
        if (track.default && !defaultFound) {
          _this2.audioTrack = id;
          defaultFound = true;
          return;
        }
        id++;
      });
      if (defaultFound === false && tracks.length) {
        _logger.logger.log('no default audio track defined, use first audio track as default');
        this.audioTrack = 0;
      }
    }
  }, {
    key: 'onAudioTrackLoaded',
    value: function onAudioTrackLoaded(data) {
      if (data.id < this.tracks.length) {
        _logger.logger.log('audioTrack ' + data.id + ' loaded');
        this.tracks[data.id].details = data.details;
        // check if current playlist is a live playlist
        if (data.details.live && !this.timer) {
          // if live playlist we will have to reload it periodically
          // set reload period to playlist target duration
          this.timer = setInterval(this.ontick, 1000 * data.details.targetduration);
        }
        if (!data.details.live && this.timer) {
          // playlist is not live and timer is armed : stopping it
          this.cleanTimer();
        }
      }
    }

    /** get alternate audio tracks list from playlist **/

  }, {
    key: 'setAudioTrackInternal',
    value: function setAudioTrackInternal(newId) {
      // check if level idx is valid
      if (newId >= 0 && newId < this.tracks.length) {
        // stopping live reloading timer if any
        this.cleanTimer();
        this.trackId = newId;
        _logger.logger.log('switching to audioTrack ' + newId);
        var audioTrack = this.tracks[newId],
            hls = this.hls,
            type = audioTrack.type,
            url = audioTrack.url,
            eventObj = { id: newId, type: type, url: url };
        // keep AUDIO_TRACK_SWITCH for legacy reason
        hls.trigger(_events2.default.AUDIO_TRACK_SWITCH, eventObj);
        hls.trigger(_events2.default.AUDIO_TRACK_SWITCHING, eventObj);
        // check if we need to load playlist for this audio Track
        var details = audioTrack.details;
        if (url && (details === undefined || details.live === true)) {
          // track not retrieved yet, or live playlist we need to (re)load it
          _logger.logger.log('(re)loading playlist for audioTrack ' + newId);
          hls.trigger(_events2.default.AUDIO_TRACK_LOADING, { url: url, id: newId });
        }
      }
    }
  }, {
    key: 'updateTrack',
    value: function updateTrack(newId) {
      // check if level idx is valid
      if (newId >= 0 && newId < this.tracks.length) {
        // stopping live reloading timer if any
        this.cleanTimer();
        this.trackId = newId;
        _logger.logger.log('updating audioTrack ' + newId);
        var audioTrack = this.tracks[newId],
            url = audioTrack.url;
        // check if we need to load playlist for this audio Track
        var details = audioTrack.details;
        if (url && (details === undefined || details.live === true)) {
          // track not retrieved yet, or live playlist we need to (re)load it
          _logger.logger.log('(re)loading playlist for audioTrack ' + newId);
          this.hls.trigger(_events2.default.AUDIO_TRACK_LOADING, { url: url, id: newId });
        }
      }
    }
  }, {
    key: 'audioTracks',
    get: function get() {
      return this.tracks;
    }

    /** get index of the selected audio track (index in audio track lists) **/

  }, {
    key: 'audioTrack',
    get: function get() {
      return this.trackId;
    }

    /** select an audio track, based on its index in audio track lists**/
    ,
    set: function set(audioTrackId) {
      if (this.trackId !== audioTrackId || this.tracks[audioTrackId].details === undefined) {
        this.setAudioTrackInternal(audioTrackId);
      }
    }
  }]);

  return AudioTrackController;
}(_eventHandler2.default);

exports.default = AudioTrackController;

},{"33":33,"34":34,"35":35,"54":54}],8:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _logger = _dereq_(54);

var _errors = _dereq_(33);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * Buffer Controller
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               */

var BufferController = function (_EventHandler) {
  _inherits(BufferController, _EventHandler);

  function BufferController(hls) {
    _classCallCheck(this, BufferController);

    // the value that we have set mediasource.duration to
    // (the actual duration may be tweaked slighly by the browser)
    var _this = _possibleConstructorReturn(this, (BufferController.__proto__ || Object.getPrototypeOf(BufferController)).call(this, hls, _events2.default.MEDIA_ATTACHING, _events2.default.MEDIA_DETACHING, _events2.default.MANIFEST_PARSED, _events2.default.BUFFER_RESET, _events2.default.BUFFER_APPENDING, _events2.default.BUFFER_CODECS, _events2.default.BUFFER_EOS, _events2.default.BUFFER_FLUSHING, _events2.default.LEVEL_PTS_UPDATED, _events2.default.LEVEL_UPDATED));

    _this._msDuration = null;
    // the value that we want to set mediaSource.duration to
    _this._levelDuration = null;

    // Source Buffer listeners
    _this.onsbue = _this.onSBUpdateEnd.bind(_this);
    _this.onsbe = _this.onSBUpdateError.bind(_this);
    _this.pendingTracks = {};
    _this.tracks = {};
    return _this;
  }

  _createClass(BufferController, [{
    key: 'destroy',
    value: function destroy() {
      _eventHandler2.default.prototype.destroy.call(this);
    }
  }, {
    key: 'onLevelPtsUpdated',
    value: function onLevelPtsUpdated(data) {
      var type = data.type;
      var audioTrack = this.tracks.audio;

      // Adjusting `SourceBuffer.timestampOffset` (desired point in the timeline where the next frames should be appended)
      // in Chrome browser when we detect MPEG audio container and time delta between level PTS and `SourceBuffer.timestampOffset`
      // is greater than 100ms (this is enough to handle seek for VOD or level change for LIVE videos). At the time of change we issue
      // `SourceBuffer.abort()` and adjusting `SourceBuffer.timestampOffset` if `SourceBuffer.updating` is false or awaiting `updateend`
      // event if SB is in updating state.
      // More info here: https://github.com/video-dev/hls.js/issues/332#issuecomment-257986486

      if (type === 'audio' && audioTrack && audioTrack.container === 'audio/mpeg') {
        // Chrome audio mp3 track
        var audioBuffer = this.sourceBuffer.audio;
        var delta = Math.abs(audioBuffer.timestampOffset - data.start);

        // adjust timestamp offset if time delta is greater than 100ms
        if (delta > 0.1) {
          var updating = audioBuffer.updating;

          try {
            audioBuffer.abort();
          } catch (err) {
            updating = true;
            _logger.logger.warn('can not abort audio buffer: ' + err);
          }

          if (!updating) {
            _logger.logger.warn('change mpeg audio timestamp offset from ' + audioBuffer.timestampOffset + ' to ' + data.start);
            audioBuffer.timestampOffset = data.start;
          } else {
            this.audioTimestampOffset = data.start;
          }
        }
      }
    }
  }, {
    key: 'onManifestParsed',
    value: function onManifestParsed(data) {
      var audioExpected = data.audio,
          videoExpected = data.video || data.levels.length && data.audio,
          sourceBufferNb = 0;
      // in case of alt audio 2 BUFFER_CODECS events will be triggered, one per stream controller
      // sourcebuffers will be created all at once when the expected nb of tracks will be reached
      // in case alt audio is not used, only one BUFFER_CODEC event will be fired from main stream controller
      // it will contain the expected nb of source buffers, no need to compute it
      if (data.altAudio && (audioExpected || videoExpected)) {
        sourceBufferNb = (audioExpected ? 1 : 0) + (videoExpected ? 1 : 0);
        _logger.logger.log(sourceBufferNb + ' sourceBuffer(s) expected');
      }
      this.sourceBufferNb = sourceBufferNb;
    }
  }, {
    key: 'onMediaAttaching',
    value: function onMediaAttaching(data) {
      var media = this.media = data.media;
      if (media) {
        // setup the media source
        var ms = this.mediaSource = new MediaSource();
        //Media Source listeners
        this.onmso = this.onMediaSourceOpen.bind(this);
        this.onmse = this.onMediaSourceEnded.bind(this);
        this.onmsc = this.onMediaSourceClose.bind(this);
        ms.addEventListener('sourceopen', this.onmso);
        ms.addEventListener('sourceended', this.onmse);
        ms.addEventListener('sourceclose', this.onmsc);
        // link video and media Source
        media.src = URL.createObjectURL(ms);
      }
    }
  }, {
    key: 'onMediaDetaching',
    value: function onMediaDetaching() {
      _logger.logger.log('media source detaching');
      var ms = this.mediaSource;
      if (ms) {
        if (ms.readyState === 'open') {
          try {
            // endOfStream could trigger exception if any sourcebuffer is in updating state
            // we don't really care about checking sourcebuffer state here,
            // as we are anyway detaching the MediaSource
            // let's just avoid this exception to propagate
            ms.endOfStream();
          } catch (err) {
            _logger.logger.warn('onMediaDetaching:' + err.message + ' while calling endOfStream');
          }
        }
        ms.removeEventListener('sourceopen', this.onmso);
        ms.removeEventListener('sourceended', this.onmse);
        ms.removeEventListener('sourceclose', this.onmsc);

        // Detach properly the MediaSource from the HTMLMediaElement as
        // suggested in https://github.com/w3c/media-source/issues/53.
        if (this.media) {
          URL.revokeObjectURL(this.media.src);
          this.media.removeAttribute('src');
          this.media.load();
        }

        this.mediaSource = null;
        this.media = null;
        this.pendingTracks = {};
        this.tracks = {};
        this.sourceBuffer = {};
        this.flushRange = [];
        this.segments = [];
        this.appended = 0;
      }
      this.onmso = this.onmse = this.onmsc = null;
      this.hls.trigger(_events2.default.MEDIA_DETACHED);
    }
  }, {
    key: 'onMediaSourceOpen',
    value: function onMediaSourceOpen() {
      _logger.logger.log('media source opened');
      this.hls.trigger(_events2.default.MEDIA_ATTACHED, { media: this.media });
      var mediaSource = this.mediaSource;
      if (mediaSource) {
        // once received, don't listen anymore to sourceopen event
        mediaSource.removeEventListener('sourceopen', this.onmso);
      }
      this.checkPendingTracks();
    }
  }, {
    key: 'checkPendingTracks',
    value: function checkPendingTracks() {
      // if any buffer codecs pending, check if we have enough to create sourceBuffers
      var pendingTracks = this.pendingTracks,
          pendingTracksNb = Object.keys(pendingTracks).length;
      // if any pending tracks and (if nb of pending tracks gt or equal than expected nb or if unknown expected nb)
      if (pendingTracksNb && (this.sourceBufferNb <= pendingTracksNb || this.sourceBufferNb === 0)) {
        // ok, let's create them now !
        this.createSourceBuffers(pendingTracks);
        this.pendingTracks = {};
        // append any pending segments now !
        this.doAppending();
      }
    }
  }, {
    key: 'onMediaSourceClose',
    value: function onMediaSourceClose() {
      _logger.logger.log('media source closed');
    }
  }, {
    key: 'onMediaSourceEnded',
    value: function onMediaSourceEnded() {
      _logger.logger.log('media source ended');
    }
  }, {
    key: 'onSBUpdateEnd',
    value: function onSBUpdateEnd() {
      // update timestampOffset
      if (this.audioTimestampOffset) {
        var audioBuffer = this.sourceBuffer.audio;
        _logger.logger.warn('change mpeg audio timestamp offset from ' + audioBuffer.timestampOffset + ' to ' + this.audioTimestampOffset);
        audioBuffer.timestampOffset = this.audioTimestampOffset;
        delete this.audioTimestampOffset;
      }

      if (this._needsFlush) {
        this.doFlush();
      }

      if (this._needsEos) {
        this.checkEos();
      }
      this.appending = false;
      var parent = this.parent;
      // count nb of pending segments waiting for appending on this sourcebuffer
      var pending = this.segments.reduce(function (counter, segment) {
        return segment.parent === parent ? counter + 1 : counter;
      }, 0);
      this.hls.trigger(_events2.default.BUFFER_APPENDED, { parent: parent, pending: pending });

      // don't append in flushing mode
      if (!this._needsFlush) {
        this.doAppending();
      }

      this.updateMediaElementDuration();
    }
  }, {
    key: 'onSBUpdateError',
    value: function onSBUpdateError(event) {
      _logger.logger.error('sourceBuffer error:', event);
      // according to http://www.w3.org/TR/media-source/#sourcebuffer-append-error
      // this error might not always be fatal (it is fatal if decode error is set, in that case
      // it will be followed by a mediaElement error ...)
      this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_APPENDING_ERROR, fatal: false });
      // we don't need to do more than that, as accordin to the spec, updateend will be fired just after
    }
  }, {
    key: 'onBufferReset',
    value: function onBufferReset() {
      var sourceBuffer = this.sourceBuffer;
      for (var type in sourceBuffer) {
        var sb = sourceBuffer[type];
        try {
          this.mediaSource.removeSourceBuffer(sb);
          sb.removeEventListener('updateend', this.onsbue);
          sb.removeEventListener('error', this.onsbe);
        } catch (err) {}
      }
      this.sourceBuffer = {};
      this.flushRange = [];
      this.segments = [];
      this.appended = 0;
    }
  }, {
    key: 'onBufferCodecs',
    value: function onBufferCodecs(tracks) {
      // if source buffer(s) not created yet, appended buffer tracks in this.pendingTracks
      // if sourcebuffers already created, do nothing ...
      if (Object.keys(this.sourceBuffer).length === 0) {
        for (var trackName in tracks) {
          this.pendingTracks[trackName] = tracks[trackName];
        }
        var mediaSource = this.mediaSource;
        if (mediaSource && mediaSource.readyState === 'open') {
          // try to create sourcebuffers if mediasource opened
          this.checkPendingTracks();
        }
      }
    }
  }, {
    key: 'createSourceBuffers',
    value: function createSourceBuffers(tracks) {
      var sourceBuffer = this.sourceBuffer,
          mediaSource = this.mediaSource;

      for (var trackName in tracks) {
        if (!sourceBuffer[trackName]) {
          var track = tracks[trackName];
          // use levelCodec as first priority
          var codec = track.levelCodec || track.codec;
          var mimeType = track.container + ';codecs=' + codec;
          _logger.logger.log('creating sourceBuffer(' + mimeType + ')');
          try {
            var sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType);
            sb.addEventListener('updateend', this.onsbue);
            sb.addEventListener('error', this.onsbe);
            this.tracks[trackName] = { codec: codec, container: track.container };
            track.buffer = sb;
          } catch (err) {
            _logger.logger.error('error while trying to add sourceBuffer:' + err.message);
            this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_ADD_CODEC_ERROR, fatal: false, err: err, mimeType: mimeType });
          }
        }
      }
      this.hls.trigger(_events2.default.BUFFER_CREATED, { tracks: tracks });
    }
  }, {
    key: 'onBufferAppending',
    value: function onBufferAppending(data) {
      if (!this._needsFlush) {
        if (!this.segments) {
          this.segments = [data];
        } else {
          this.segments.push(data);
        }
        this.doAppending();
      }
    }
  }, {
    key: 'onBufferAppendFail',
    value: function onBufferAppendFail(data) {
      _logger.logger.error('sourceBuffer error:', data.event);
      // according to http://www.w3.org/TR/media-source/#sourcebuffer-append-error
      // this error might not always be fatal (it is fatal if decode error is set, in that case
      // it will be followed by a mediaElement error ...)
      this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_APPENDING_ERROR, fatal: false });
    }

    // on BUFFER_EOS mark matching sourcebuffer(s) as ended and trigger checkEos()

  }, {
    key: 'onBufferEos',
    value: function onBufferEos(data) {
      var sb = this.sourceBuffer;
      var dataType = data.type;
      for (var type in sb) {
        if (!dataType || type === dataType) {
          if (!sb[type].ended) {
            sb[type].ended = true;
            _logger.logger.log(type + ' sourceBuffer now EOS');
          }
        }
      }
      this.checkEos();
    }

    // if all source buffers are marked as ended, signal endOfStream() to MediaSource.

  }, {
    key: 'checkEos',
    value: function checkEos() {
      var sb = this.sourceBuffer,
          mediaSource = this.mediaSource;
      if (!mediaSource || mediaSource.readyState !== 'open') {
        this._needsEos = false;
        return;
      }
      for (var type in sb) {
        var sbobj = sb[type];
        if (!sbobj.ended) {
          return;
        }
        if (sbobj.updating) {
          this._needsEos = true;
          return;
        }
      }
      _logger.logger.log('all media data available, signal endOfStream() to MediaSource and stop loading fragment');
      //Notify the media element that it now has all of the media data
      try {
        mediaSource.endOfStream();
      } catch (e) {
        _logger.logger.warn('exception while calling mediaSource.endOfStream()');
      }
      this._needsEos = false;
    }
  }, {
    key: 'onBufferFlushing',
    value: function onBufferFlushing(data) {
      this.flushRange.push({ start: data.startOffset, end: data.endOffset, type: data.type });
      // attempt flush immediatly
      this.flushBufferCounter = 0;
      this.doFlush();
    }
  }, {
    key: 'onLevelUpdated',
    value: function onLevelUpdated(event) {
      var details = event.details;
      if (details.fragments.length === 0) {
        return;
      }
      this._levelDuration = details.totalduration + details.fragments[0].start;
      this.updateMediaElementDuration();
    }

    // https://github.com/video-dev/hls.js/issues/355

  }, {
    key: 'updateMediaElementDuration',
    value: function updateMediaElementDuration() {
      var media = this.media,
          mediaSource = this.mediaSource,
          sourceBuffer = this.sourceBuffer,
          levelDuration = this._levelDuration;
      if (levelDuration === null || !media || !mediaSource || !sourceBuffer || media.readyState === 0 || mediaSource.readyState !== 'open') {
        return;
      }
      for (var type in sourceBuffer) {
        if (sourceBuffer[type].updating) {
          // can't set duration whilst a buffer is updating
          return;
        }
      }
      if (this._msDuration === null) {
        // initialise to the value that the media source is reporting
        this._msDuration = mediaSource.duration;
      }
      var duration = media.duration;
      // levelDuration was the last value we set.
      // not using mediaSource.duration as the browser may tweak this value
      // only update mediasource duration if its value increase, this is to avoid
      // flushing already buffered portion when switching between quality level
      if (levelDuration > this._msDuration && levelDuration > duration || duration === Infinity || isNaN(duration)) {
        _logger.logger.log('Updating mediasource duration to ' + levelDuration.toFixed(3));
        this._msDuration = mediaSource.duration = levelDuration;
      }
    }
  }, {
    key: 'doFlush',
    value: function doFlush() {
      // loop through all buffer ranges to flush
      while (this.flushRange.length) {
        var range = this.flushRange[0];
        // flushBuffer will abort any buffer append in progress and flush Audio/Video Buffer
        if (this.flushBuffer(range.start, range.end, range.type)) {
          // range flushed, remove from flush array
          this.flushRange.shift();
          this.flushBufferCounter = 0;
        } else {
          this._needsFlush = true;
          // avoid looping, wait for SB update end to retrigger a flush
          return;
        }
      }
      if (this.flushRange.length === 0) {
        // everything flushed
        this._needsFlush = false;

        // let's recompute this.appended, which is used to avoid flush looping
        var appended = 0;
        var sourceBuffer = this.sourceBuffer;
        try {
          for (var type in sourceBuffer) {
            appended += sourceBuffer[type].buffered.length;
          }
        } catch (error) {
          // error could be thrown while accessing buffered, in case sourcebuffer has already been removed from MediaSource
          // this is harmess at this stage, catch this to avoid reporting an internal exception
          _logger.logger.error('error while accessing sourceBuffer.buffered');
        }
        this.appended = appended;
        this.hls.trigger(_events2.default.BUFFER_FLUSHED);
      }
    }
  }, {
    key: 'doAppending',
    value: function doAppending() {
      var hls = this.hls,
          sourceBuffer = this.sourceBuffer,
          segments = this.segments;
      if (Object.keys(sourceBuffer).length) {
        if (this.media.error) {
          this.segments = [];
          _logger.logger.error('trying to append although a media error occured, flush segment and abort');
          return;
        }
        if (this.appending) {
          //logger.log(`sb appending in progress`);
          return;
        }
        if (segments && segments.length) {
          var segment = segments.shift();
          try {
            var type = segment.type,
                sb = sourceBuffer[type];
            if (sb) {
              if (!sb.updating) {
                // reset sourceBuffer ended flag before appending segment
                sb.ended = false;
                //logger.log(`appending ${segment.content} ${type} SB, size:${segment.data.length}, ${segment.parent}`);
                this.parent = segment.parent;
                sb.appendBuffer(segment.data);
                this.appendError = 0;
                this.appended++;
                this.appending = true;
              } else {
                segments.unshift(segment);
              }
            } else {
              // in case we don't have any source buffer matching with this segment type,
              // it means that Mediasource fails to create sourcebuffer
              // discard this segment, and trigger update end
              this.onSBUpdateEnd();
            }
          } catch (err) {
            // in case any error occured while appending, put back segment in segments table
            _logger.logger.error('error while trying to append buffer:' + err.message);
            segments.unshift(segment);
            var event = { type: _errors.ErrorTypes.MEDIA_ERROR, parent: segment.parent };
            if (err.code !== 22) {
              if (this.appendError) {
                this.appendError++;
              } else {
                this.appendError = 1;
              }
              event.details = _errors.ErrorDetails.BUFFER_APPEND_ERROR;
              /* with UHD content, we could get loop of quota exceeded error until
                browser is able to evict some data from sourcebuffer. retrying help recovering this
              */
              if (this.appendError > hls.config.appendErrorMaxRetry) {
                _logger.logger.log('fail ' + hls.config.appendErrorMaxRetry + ' times to append segment in sourceBuffer');
                segments = [];
                event.fatal = true;
                hls.trigger(_events2.default.ERROR, event);
                return;
              } else {
                event.fatal = false;
                hls.trigger(_events2.default.ERROR, event);
              }
            } else {
              // QuotaExceededError: http://www.w3.org/TR/html5/infrastructure.html#quotaexceedederror
              // let's stop appending any segments, and report BUFFER_FULL_ERROR error
              this.segments = [];
              event.details = _errors.ErrorDetails.BUFFER_FULL_ERROR;
              event.fatal = false;
              hls.trigger(_events2.default.ERROR, event);
              return;
            }
          }
        }
      }
    }

    /*
      flush specified buffered range,
      return true once range has been flushed.
      as sourceBuffer.remove() is asynchronous, flushBuffer will be retriggered on sourceBuffer update end
    */

  }, {
    key: 'flushBuffer',
    value: function flushBuffer(startOffset, endOffset, typeIn) {
      var sb,
          i,
          bufStart,
          bufEnd,
          flushStart,
          flushEnd,
          sourceBuffer = this.sourceBuffer;
      if (Object.keys(sourceBuffer).length) {
        _logger.logger.log('flushBuffer,pos/start/end: ' + this.media.currentTime.toFixed(3) + '/' + startOffset + '/' + endOffset);
        // safeguard to avoid infinite looping : don't try to flush more than the nb of appended segments
        if (this.flushBufferCounter < this.appended) {
          for (var type in sourceBuffer) {
            // check if sourcebuffer type is defined (typeIn): if yes, let's only flush this one
            // if no, let's flush all sourcebuffers
            if (typeIn && type !== typeIn) {
              continue;
            }
            sb = sourceBuffer[type];
            // we are going to flush buffer, mark source buffer as 'not ended'
            sb.ended = false;
            if (!sb.updating) {
              try {
                for (i = 0; i < sb.buffered.length; i++) {
                  bufStart = sb.buffered.start(i);
                  bufEnd = sb.buffered.end(i);
                  // workaround firefox not able to properly flush multiple buffered range.
                  if (navigator.userAgent.toLowerCase().indexOf('firefox') !== -1 && endOffset === Number.POSITIVE_INFINITY) {
                    flushStart = startOffset;
                    flushEnd = endOffset;
                  } else {
                    flushStart = Math.max(bufStart, startOffset);
                    flushEnd = Math.min(bufEnd, endOffset);
                  }
                  /* sometimes sourcebuffer.remove() does not flush
                     the exact expected time range.
                     to avoid rounding issues/infinite loop,
                     only flush buffer range of length greater than 500ms.
                  */
                  if (Math.min(flushEnd, bufEnd) - flushStart > 0.5) {
                    this.flushBufferCounter++;
                    _logger.logger.log('flush ' + type + ' [' + flushStart + ',' + flushEnd + '], of [' + bufStart + ',' + bufEnd + '], pos:' + this.media.currentTime);
                    sb.remove(flushStart, flushEnd);
                    return false;
                  }
                }
              } catch (e) {
                _logger.logger.warn('exception while accessing sourcebuffer, it might have been removed from MediaSource');
              }
            } else {
              //logger.log('abort ' + type + ' append in progress');
              // this will abort any appending in progress
              //sb.abort();
              _logger.logger.warn('cannot flush, sb updating in progress');
              return false;
            }
          }
        } else {
          _logger.logger.warn('abort flushing too many retries');
        }
        _logger.logger.log('buffer flushed');
      }
      // everything flushed !
      return true;
    }
  }]);

  return BufferController;
}(_eventHandler2.default);

exports.default = BufferController;

},{"33":33,"34":34,"35":35,"54":54}],9:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * cap stream level to media size dimension controller
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               */

var CapLevelController = function (_EventHandler) {
  _inherits(CapLevelController, _EventHandler);

  function CapLevelController(hls) {
    _classCallCheck(this, CapLevelController);

    return _possibleConstructorReturn(this, (CapLevelController.__proto__ || Object.getPrototypeOf(CapLevelController)).call(this, hls, _events2.default.FPS_DROP_LEVEL_CAPPING, _events2.default.MEDIA_ATTACHING, _events2.default.MANIFEST_PARSED));
  }

  _createClass(CapLevelController, [{
    key: 'destroy',
    value: function destroy() {
      if (this.hls.config.capLevelToPlayerSize) {
        this.media = this.restrictedLevels = null;
        this.autoLevelCapping = Number.POSITIVE_INFINITY;
        if (this.timer) {
          this.timer = clearInterval(this.timer);
        }
      }
    }
  }, {
    key: 'onFpsDropLevelCapping',
    value: function onFpsDropLevelCapping(data) {
      // Don't add a restricted level more than once
      if (CapLevelController.isLevelAllowed(data.droppedLevel, this.restrictedLevels)) {
        this.restrictedLevels.push(data.droppedLevel);
      }
    }
  }, {
    key: 'onMediaAttaching',
    value: function onMediaAttaching(data) {
      this.media = data.media instanceof HTMLVideoElement ? data.media : null;
    }
  }, {
    key: 'onManifestParsed',
    value: function onManifestParsed(data) {
      var hls = this.hls;
      this.restrictedLevels = [];
      if (hls.config.capLevelToPlayerSize) {
        this.autoLevelCapping = Number.POSITIVE_INFINITY;
        this.levels = data.levels;
        hls.firstLevel = this.getMaxLevel(data.firstLevel);
        clearInterval(this.timer);
        this.timer = setInterval(this.detectPlayerSize.bind(this), 1000);
        this.detectPlayerSize();
      }
    }
  }, {
    key: 'detectPlayerSize',
    value: function detectPlayerSize() {
      if (this.media) {
        var levelsLength = this.levels ? this.levels.length : 0;
        if (levelsLength) {
          var hls = this.hls;
          hls.autoLevelCapping = this.getMaxLevel(levelsLength - 1);
          if (hls.autoLevelCapping > this.autoLevelCapping) {
            // if auto level capping has a higher value for the previous one, flush the buffer using nextLevelSwitch
            // usually happen when the user go to the fullscreen mode.
            hls.streamController.nextLevelSwitch();
          }
          this.autoLevelCapping = hls.autoLevelCapping;
        }
      }
    }

    /*
    * returns level should be the one with the dimensions equal or greater than the media (player) dimensions (so the video will be downscaled)
    */

  }, {
    key: 'getMaxLevel',
    value: function getMaxLevel(capLevelIndex) {
      var _this2 = this;

      if (!this.levels) {
        return -1;
      }

      var validLevels = this.levels.filter(function (level, index) {
        return CapLevelController.isLevelAllowed(index, _this2.restrictedLevels) && index <= capLevelIndex;
      });

      return CapLevelController.getMaxLevelByMediaSize(validLevels, this.mediaWidth, this.mediaHeight);
    }
  }, {
    key: 'mediaWidth',
    get: function get() {
      var width = void 0;
      var media = this.media;
      if (media) {
        width = media.width || media.clientWidth || media.offsetWidth;
        width *= CapLevelController.contentScaleFactor;
      }
      return width;
    }
  }, {
    key: 'mediaHeight',
    get: function get() {
      var height = void 0;
      var media = this.media;
      if (media) {
        height = media.height || media.clientHeight || media.offsetHeight;
        height *= CapLevelController.contentScaleFactor;
      }
      return height;
    }
  }], [{
    key: 'isLevelAllowed',
    value: function isLevelAllowed(level) {
      var restrictedLevels = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];

      return restrictedLevels.indexOf(level) === -1;
    }
  }, {
    key: 'getMaxLevelByMediaSize',
    value: function getMaxLevelByMediaSize(levels, width, height) {
      if (!levels || levels && !levels.length) {
        return -1;
      }

      // Levels can have the same dimensions but differing bandwidths - since levels are ordered, we can look to the next
      // to determine whether we've chosen the greatest bandwidth for the media's dimensions
      var atGreatestBandiwdth = function atGreatestBandiwdth(curLevel, nextLevel) {
        if (!nextLevel) {
          return true;
        }
        return curLevel.width !== nextLevel.width || curLevel.height !== nextLevel.height;
      };

      // If we run through the loop without breaking, the media's dimensions are greater than every level, so default to
      // the max level
      var maxLevelIndex = levels.length - 1;

      for (var i = 0; i < levels.length; i += 1) {
        var level = levels[i];
        if ((level.width >= width || level.height >= height) && atGreatestBandiwdth(level, levels[i + 1])) {
          maxLevelIndex = i;
          break;
        }
      }

      return maxLevelIndex;
    }
  }, {
    key: 'contentScaleFactor',
    get: function get() {
      var pixelRatio = 1;
      try {
        pixelRatio = window.devicePixelRatio;
      } catch (e) {}
      return pixelRatio;
    }
  }]);

  return CapLevelController;
}(_eventHandler2.default);

exports.default = CapLevelController;

},{"34":34,"35":35}],10:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _logger = _dereq_(54);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * FPS Controller
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               */

var FPSController = function (_EventHandler) {
  _inherits(FPSController, _EventHandler);

  function FPSController(hls) {
    _classCallCheck(this, FPSController);

    return _possibleConstructorReturn(this, (FPSController.__proto__ || Object.getPrototypeOf(FPSController)).call(this, hls, _events2.default.MEDIA_ATTACHING));
  }

  _createClass(FPSController, [{
    key: 'destroy',
    value: function destroy() {
      if (this.timer) {
        clearInterval(this.timer);
      }
      this.isVideoPlaybackQualityAvailable = false;
    }
  }, {
    key: 'onMediaAttaching',
    value: function onMediaAttaching(data) {
      var config = this.hls.config;
      if (config.capLevelOnFPSDrop) {
        var video = this.video = data.media instanceof HTMLVideoElement ? data.media : null;
        if (typeof video.getVideoPlaybackQuality === 'function') {
          this.isVideoPlaybackQualityAvailable = true;
        }
        clearInterval(this.timer);
        this.timer = setInterval(this.checkFPSInterval.bind(this), config.fpsDroppedMonitoringPeriod);
      }
    }
  }, {
    key: 'checkFPS',
    value: function checkFPS(video, decodedFrames, droppedFrames) {
      var currentTime = performance.now();
      if (decodedFrames) {
        if (this.lastTime) {
          var currentPeriod = currentTime - this.lastTime,
              currentDropped = droppedFrames - this.lastDroppedFrames,
              currentDecoded = decodedFrames - this.lastDecodedFrames,
              droppedFPS = 1000 * currentDropped / currentPeriod,
              hls = this.hls;
          hls.trigger(_events2.default.FPS_DROP, { currentDropped: currentDropped, currentDecoded: currentDecoded, totalDroppedFrames: droppedFrames });
          if (droppedFPS > 0) {
            //logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod));
            if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) {
              var currentLevel = hls.currentLevel;
              _logger.logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel);
              if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) {
                currentLevel = currentLevel - 1;
                hls.trigger(_events2.default.FPS_DROP_LEVEL_CAPPING, { level: currentLevel, droppedLevel: hls.currentLevel });
                hls.autoLevelCapping = currentLevel;
                hls.streamController.nextLevelSwitch();
              }
            }
          }
        }
        this.lastTime = currentTime;
        this.lastDroppedFrames = droppedFrames;
        this.lastDecodedFrames = decodedFrames;
      }
    }
  }, {
    key: 'checkFPSInterval',
    value: function checkFPSInterval() {
      var video = this.video;
      if (video) {
        if (this.isVideoPlaybackQualityAvailable) {
          var videoPlaybackQuality = video.getVideoPlaybackQuality();
          this.checkFPS(video, videoPlaybackQuality.totalVideoFrames, videoPlaybackQuality.droppedVideoFrames);
        } else {
          this.checkFPS(video, video.webkitDecodedFrameCount, video.webkitDroppedFrameCount);
        }
      }
    }
  }]);

  return FPSController;
}(_eventHandler2.default);

exports.default = FPSController;

},{"34":34,"35":35,"54":54}],11:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _id = _dereq_(27);

var _id2 = _interopRequireDefault(_id);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * id3 metadata track controller
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               */

var ID3TrackController = function (_EventHandler) {
  _inherits(ID3TrackController, _EventHandler);

  function ID3TrackController(hls) {
    _classCallCheck(this, ID3TrackController);

    var _this = _possibleConstructorReturn(this, (ID3TrackController.__proto__ || Object.getPrototypeOf(ID3TrackController)).call(this, hls, _events2.default.MEDIA_ATTACHED, _events2.default.MEDIA_DETACHING, _events2.default.FRAG_PARSING_METADATA));

    _this.id3Track = undefined;
    _this.media = undefined;
    return _this;
  }

  _createClass(ID3TrackController, [{
    key: 'destroy',
    value: function destroy() {
      _eventHandler2.default.prototype.destroy.call(this);
    }

    // Add ID3 metatadata text track.

  }, {
    key: 'onMediaAttached',
    value: function onMediaAttached(data) {
      this.media = data.media;
      if (!this.media) {
        return;
      }

      this.id3Track = this.media.addTextTrack('metadata', 'id3');
      this.id3Track.mode = 'hidden';
    }
  }, {
    key: 'onMediaDetaching',
    value: function onMediaDetaching() {
      this.media = undefined;
    }
  }, {
    key: 'onFragParsingMetadata',
    value: function onFragParsingMetadata(data) {
      var fragment = data.frag;
      var samples = data.samples;

      // Attempt to recreate Safari functionality by creating
      // WebKitDataCue objects when available and store the decoded
      // ID3 data in the value property of the cue
      var Cue = window.WebKitDataCue || window.VTTCue || window.TextTrackCue;

      for (var i = 0; i < samples.length; i++) {
        var frames = _id2.default.getID3Frames(samples[i].data);
        if (frames) {
          var startTime = samples[i].pts;
          var endTime = i < samples.length - 1 ? samples[i + 1].pts : fragment.endPTS;

          // Give a slight bump to the endTime if it's equal to startTime to avoid a SyntaxError in IE
          if (startTime === endTime) {
            endTime += 0.0001;
          }

          for (var j = 0; j < frames.length; j++) {
            var frame = frames[j];
            // Safari doesn't put the timestamp frame in the TextTrack
            if (!_id2.default.isTimeStampFrame(frame)) {
              var cue = new Cue(startTime, endTime, '');
              cue.value = frame;
              this.id3Track.addCue(cue);
            }
          }
        }
      }
    }
  }]);

  return ID3TrackController;
}(_eventHandler2.default);

exports.default = ID3TrackController;

},{"27":27,"34":34,"35":35}],12:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _logger = _dereq_(54);

var _errors = _dereq_(33);

var _bufferHelper = _dereq_(37);

var _bufferHelper2 = _interopRequireDefault(_bufferHelper);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * Level Controller
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               */

var LevelController = function (_EventHandler) {
  _inherits(LevelController, _EventHandler);

  function LevelController(hls) {
    _classCallCheck(this, LevelController);

    var _this = _possibleConstructorReturn(this, (LevelController.__proto__ || Object.getPrototypeOf(LevelController)).call(this, hls, _events2.default.MANIFEST_LOADED, _events2.default.LEVEL_LOADED, _events2.default.FRAG_LOADED, _events2.default.ERROR));

    _this.ontick = _this.tick.bind(_this);
    _this._manualLevel = -1;
    return _this;
  }

  _createClass(LevelController, [{
    key: 'destroy',
    value: function destroy() {
      this.cleanTimer();
      this._manualLevel = -1;
    }
  }, {
    key: 'cleanTimer',
    value: function cleanTimer() {
      if (this.timer) {
        clearTimeout(this.timer);
        this.timer = null;
      }
    }
  }, {
    key: 'startLoad',
    value: function startLoad() {
      this.canload = true;
      var levels = this._levels;
      // clean up live level details to force reload them, and reset load errors
      if (levels) {
        levels.forEach(function (level) {
          level.loadError = 0;
          var levelDetails = level.details;
          if (levelDetails && levelDetails.live) {
            level.details = undefined;
          }
        });
      }
      // speed up live playlist refresh if timer exists
      if (this.timer) {
        this.tick();
      }
    }
  }, {
    key: 'stopLoad',
    value: function stopLoad() {
      this.canload = false;
    }
  }, {
    key: 'onManifestLoaded',
    value: function onManifestLoaded(data) {
      var levels0 = [],
          levels = [],
          bitrateStart,
          bitrateSet = {},
          videoCodecFound = false,
          audioCodecFound = false,
          hls = this.hls,
          brokenmp4inmp3 = /chrome|firefox/.test(navigator.userAgent.toLowerCase()),
          checkSupported = function checkSupported(type, codec) {
        return MediaSource.isTypeSupported(type + '/mp4;codecs=' + codec);
      };

      // regroup redundant level together
      data.levels.forEach(function (level) {
        if (level.videoCodec) {
          videoCodecFound = true;
        }
        // erase audio codec info if browser does not support mp4a.40.34. demuxer will autodetect codec and fallback to mpeg/audio
        if (brokenmp4inmp3 && level.audioCodec && level.audioCodec.indexOf('mp4a.40.34') !== -1) {
          level.audioCodec = undefined;
        }
        if (level.audioCodec || level.attrs && level.attrs.AUDIO) {
          audioCodecFound = true;
        }
        var redundantLevelId = bitrateSet[level.bitrate];
        if (redundantLevelId === undefined) {
          bitrateSet[level.bitrate] = levels0.length;
          level.url = [level.url];
          level.urlId = 0;
          levels0.push(level);
        } else {
          levels0[redundantLevelId].url.push(level.url);
        }
      });

      // remove audio-only level if we also have levels with audio+video codecs signalled
      if (videoCodecFound && audioCodecFound) {
        levels0.forEach(function (level) {
          if (level.videoCodec) {
            levels.push(level);
          }
        });
      } else {
        levels = levels0;
      }
      // only keep level with supported audio/video codecs
      levels = levels.filter(function (level) {
        var audioCodec = level.audioCodec,
            videoCodec = level.videoCodec;
        return (!audioCodec || checkSupported('audio', audioCodec)) && (!videoCodec || checkSupported('video', videoCodec));
      });

      if (levels.length) {
        // start bitrate is the first bitrate of the manifest
        bitrateStart = levels[0].bitrate;
        // sort level on bitrate
        levels.sort(function (a, b) {
          return a.bitrate - b.bitrate;
        });
        this._levels = levels;
        // find index of first level in sorted levels
        for (var i = 0; i < levels.length; i++) {
          if (levels[i].bitrate === bitrateStart) {
            this._firstLevel = i;
            _logger.logger.log('manifest loaded,' + levels.length + ' level(s) found, first bitrate:' + bitrateStart);
            break;
          }
        }
        hls.trigger(_events2.default.MANIFEST_PARSED, { levels: levels, firstLevel: this._firstLevel, stats: data.stats, audio: audioCodecFound, video: videoCodecFound, altAudio: data.audioTracks.length > 0 });
      } else {
        hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR, fatal: true, url: hls.url, reason: 'no level with compatible codecs found in manifest' });
      }
      return;
    }
  }, {
    key: 'setLevelInternal',
    value: function setLevelInternal(newLevel) {
      var levels = this._levels;
      var hls = this.hls;
      // check if level idx is valid
      if (newLevel >= 0 && newLevel < levels.length) {
        // stopping live reloading timer if any
        this.cleanTimer();
        if (this._level !== newLevel) {
          _logger.logger.log('switching to level ' + newLevel);
          this._level = newLevel;
          var levelProperties = levels[newLevel];
          levelProperties.level = newLevel;
          // LEVEL_SWITCH to be deprecated in next major release
          hls.trigger(_events2.default.LEVEL_SWITCH, levelProperties);
          hls.trigger(_events2.default.LEVEL_SWITCHING, levelProperties);
        }
        var level = levels[newLevel],
            levelDetails = level.details;
        // check if we need to load playlist for this level
        if (!levelDetails || levelDetails.live === true) {
          // level not retrieved yet, or live playlist we need to (re)load it
          var urlId = level.urlId;
          hls.trigger(_events2.default.LEVEL_LOADING, { url: level.url[urlId], level: newLevel, id: urlId });
        }
      } else {
        // invalid level id given, trigger error
        hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.OTHER_ERROR, details: _errors.ErrorDetails.LEVEL_SWITCH_ERROR, level: newLevel, fatal: false, reason: 'invalid level idx' });
      }
    }
  }, {
    key: 'onError',
    value: function onError(data) {
      if (data.fatal) {
        if (data.type === _errors.ErrorTypes.NETWORK_ERROR) {
          this.cleanTimer();
        }
        return;
      }

      var details = data.details,
          hls = this.hls,
          levelId = void 0,
          level = void 0,
          levelError = false;
      // try to recover not fatal errors
      switch (details) {
        case _errors.ErrorDetails.FRAG_LOAD_ERROR:
        case _errors.ErrorDetails.FRAG_LOAD_TIMEOUT:
        case _errors.ErrorDetails.FRAG_LOOP_LOADING_ERROR:
        case _errors.ErrorDetails.KEY_LOAD_ERROR:
        case _errors.ErrorDetails.KEY_LOAD_TIMEOUT:
          levelId = data.frag.level;
          break;
        case _errors.ErrorDetails.LEVEL_LOAD_ERROR:
        case _errors.ErrorDetails.LEVEL_LOAD_TIMEOUT:
          levelId = data.context.level;
          levelError = true;
          break;
        case _errors.ErrorDetails.REMUX_ALLOC_ERROR:
          levelId = data.level;
          break;
        default:
          break;
      }
      /* try to switch to a redundant stream if any available.
       * if no redundant stream available, emergency switch down (if in auto mode and current level not 0)
       * otherwise, we cannot recover this network error ...
       */
      if (levelId !== undefined) {
        level = this._levels[levelId];
        if (!level.loadError) {
          level.loadError = 1;
        } else {
          level.loadError++;
        }
        // if any redundant streams available and if we haven't try them all (level.loadError is reseted on successful frag/level load.
        // if level.loadError reaches nbRedundantLevel it means that we tried them all, no hope  => let's switch down
        var nbRedundantLevel = level.url.length;
        if (nbRedundantLevel > 1 && level.loadError < nbRedundantLevel) {
          level.urlId = (level.urlId + 1) % nbRedundantLevel;
          level.details = undefined;
          _logger.logger.warn('level controller,' + details + ' for level ' + levelId + ': switching to redundant stream id ' + level.urlId);
        } else {
          // we could try to recover if in auto mode and current level not lowest level (0)
          var recoverable = this._manualLevel === -1 && levelId;
          if (recoverable) {
            _logger.logger.warn('level controller,' + details + ': switch-down for next fragment');
            hls.nextAutoLevel = Math.max(0, levelId - 1);
          } else if (level && level.details && level.details.live) {
            _logger.logger.warn('level controller,' + details + ' on live stream, discard');
            if (levelError) {
              // reset this._level so that another call to set level() will retrigger a frag load
              this._level = undefined;
            }
            // other errors are handled by stream controller
          } else if (details === _errors.ErrorDetails.LEVEL_LOAD_ERROR || details === _errors.ErrorDetails.LEVEL_LOAD_TIMEOUT) {
            var media = hls.media,

            // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end
            mediaBuffered = media && _bufferHelper2.default.isBuffered(media, media.currentTime) && _bufferHelper2.default.isBuffered(media, media.currentTime + 0.5);
            if (mediaBuffered) {
              var retryDelay = hls.config.levelLoadingRetryDelay;
              _logger.logger.warn('level controller,' + details + ', but media buffered, retry in ' + retryDelay + 'ms');
              this.timer = setTimeout(this.ontick, retryDelay);
              // boolean used to inform stream controller not to switch back to IDLE on non fatal error
              data.levelRetry = true;
            } else {
              _logger.logger.error('cannot recover ' + details + ' error');
              this._level = undefined;
              // stopping live reloading timer if any
              this.cleanTimer();
              // switch error to fatal
              data.fatal = true;
            }
          }
        }
      }
    }

    // reset level load error counter on successful frag loaded

  }, {
    key: 'onFragLoaded',
    value: function onFragLoaded(data) {
      var fragLoaded = data.frag;
      if (fragLoaded && fragLoaded.type === 'main') {
        var level = this._levels[fragLoaded.level];
        if (level) {
          level.loadError = 0;
        }
      }
    }
  }, {
    key: 'onLevelLoaded',
    value: function onLevelLoaded(data) {
      var levelId = data.level;
      // only process level loaded events matching with expected level
      if (levelId === this._level) {
        var curLevel = this._levels[levelId];
        // reset level load error counter on successful level loaded
        curLevel.loadError = 0;
        var newDetails = data.details;
        // if current playlist is a live playlist, arm a timer to reload it
        if (newDetails.live) {
          var reloadInterval = 1000 * (newDetails.averagetargetduration ? newDetails.averagetargetduration : newDetails.targetduration),
              curDetails = curLevel.details;
          if (curDetails && newDetails.endSN === curDetails.endSN) {
            // follow HLS Spec, If the client reloads a Playlist file and finds that it has not
            // changed then it MUST wait for a period of one-half the target
            // duration before retrying.
            reloadInterval /= 2;
            _logger.logger.log('same live playlist, reload twice faster');
          }
          // decrement reloadInterval with level loading delay
          reloadInterval -= performance.now() - data.stats.trequest;
          // in any case, don't reload more than every second
          reloadInterval = Math.max(1000, Math.round(reloadInterval));
          _logger.logger.log('live playlist, reload in ' + reloadInterval + ' ms');
          this.timer = setTimeout(this.ontick, reloadInterval);
        } else {
          this.timer = null;
        }
      }
    }
  }, {
    key: 'tick',
    value: function tick() {
      var levelId = this._level;
      if (levelId !== undefined && this.canload) {
        var level = this._levels[levelId];
        if (level && level.url) {
          var urlId = level.urlId;
          this.hls.trigger(_events2.default.LEVEL_LOADING, { url: level.url[urlId], level: levelId, id: urlId });
        }
      }
    }
  }, {
    key: 'levels',
    get: function get() {
      return this._levels;
    }
  }, {
    key: 'level',
    get: function get() {
      return this._level;
    },
    set: function set(newLevel) {
      var levels = this._levels;
      if (levels && levels.length > newLevel) {
        if (this._level !== newLevel || levels[newLevel].details === undefined) {
          this.setLevelInternal(newLevel);
        }
      }
    }
  }, {
    key: 'manualLevel',
    get: function get() {
      return this._manualLevel;
    },
    set: function set(newLevel) {
      this._manualLevel = newLevel;
      if (this._startLevel === undefined) {
        this._startLevel = newLevel;
      }
      if (newLevel !== -1) {
        this.level = newLevel;
      }
    }
  }, {
    key: 'firstLevel',
    get: function get() {
      return this._firstLevel;
    },
    set: function set(newLevel) {
      this._firstLevel = newLevel;
    }
  }, {
    key: 'startLevel',
    get: function get() {
      // hls.startLevel takes precedence over config.startLevel
      // if none of these values are defined, fallback on this._firstLevel (first quality level appearing in variant manifest)
      if (this._startLevel === undefined) {
        var configStartLevel = this.hls.config.startLevel;
        if (configStartLevel !== undefined) {
          return configStartLevel;
        } else {
          return this._firstLevel;
        }
      } else {
        return this._startLevel;
      }
    },
    set: function set(newLevel) {
      this._startLevel = newLevel;
    }
  }, {
    key: 'nextLoadLevel',
    get: function get() {
      if (this._manualLevel !== -1) {
        return this._manualLevel;
      } else {
        return this.hls.nextAutoLevel;
      }
    },
    set: function set(nextLevel) {
      this.level = nextLevel;
      if (this._manualLevel === -1) {
        this.hls.nextAutoLevel = nextLevel;
      }
    }
  }]);

  return LevelController;
}(_eventHandler2.default);

exports.default = LevelController;

},{"33":33,"34":34,"35":35,"37":37,"54":54}],13:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _binarySearch = _dereq_(48);

var _binarySearch2 = _interopRequireDefault(_binarySearch);

var _bufferHelper = _dereq_(37);

var _bufferHelper2 = _interopRequireDefault(_bufferHelper);

var _demuxer = _dereq_(25);

var _demuxer2 = _interopRequireDefault(_demuxer);

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _levelHelper = _dereq_(38);

var _levelHelper2 = _interopRequireDefault(_levelHelper);

var _timeRanges = _dereq_(55);

var _timeRanges2 = _interopRequireDefault(_timeRanges);

var _errors = _dereq_(33);

var _logger = _dereq_(54);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * Stream Controller
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               */

var State = {
  STOPPED: 'STOPPED',
  IDLE: 'IDLE',
  KEY_LOADING: 'KEY_LOADING',
  FRAG_LOADING: 'FRAG_LOADING',
  FRAG_LOADING_WAITING_RETRY: 'FRAG_LOADING_WAITING_RETRY',
  WAITING_LEVEL: 'WAITING_LEVEL',
  PARSING: 'PARSING',
  PARSED: 'PARSED',
  BUFFER_FLUSHING: 'BUFFER_FLUSHING',
  ENDED: 'ENDED',
  ERROR: 'ERROR'
};

var StreamController = function (_EventHandler) {
  _inherits(StreamController, _EventHandler);

  function StreamController(hls) {
    _classCallCheck(this, StreamController);

    var _this = _possibleConstructorReturn(this, (StreamController.__proto__ || Object.getPrototypeOf(StreamController)).call(this, hls, _events2.default.MEDIA_ATTACHED, _events2.default.MEDIA_DETACHING, _events2.default.MANIFEST_LOADING, _events2.default.MANIFEST_PARSED, _events2.default.LEVEL_LOADED, _events2.default.KEY_LOADED, _events2.default.FRAG_LOADED, _events2.default.FRAG_LOAD_EMERGENCY_ABORTED, _events2.default.FRAG_PARSING_INIT_SEGMENT, _events2.default.FRAG_PARSING_DATA, _events2.default.FRAG_PARSED, _events2.default.ERROR, _events2.default.AUDIO_TRACK_SWITCHING, _events2.default.AUDIO_TRACK_SWITCHED, _events2.default.BUFFER_CREATED, _events2.default.BUFFER_APPENDED, _events2.default.BUFFER_FLUSHED));

    _this.config = hls.config;
    _this.audioCodecSwap = false;
    _this.ticks = 0;
    _this._state = State.STOPPED;
    _this.ontick = _this.tick.bind(_this);
    return _this;
  }

  _createClass(StreamController, [{
    key: 'destroy',
    value: function destroy() {
      this.stopLoad();
      if (this.timer) {
        clearInterval(this.timer);
        this.timer = null;
      }
      _eventHandler2.default.prototype.destroy.call(this);
      this.state = State.STOPPED;
    }
  }, {
    key: 'startLoad',
    value: function startLoad(startPosition) {
      if (this.levels) {
        var lastCurrentTime = this.lastCurrentTime,
            hls = this.hls;
        this.stopLoad();
        if (!this.timer) {
          this.timer = setInterval(this.ontick, 100);
        }
        this.level = -1;
        this.fragLoadError = 0;
        if (!this.startFragRequested) {
          // determine load level
          var startLevel = hls.startLevel;
          if (startLevel === -1) {
            // -1 : guess start Level by doing a bitrate test by loading first fragment of lowest quality level
            startLevel = 0;
            this.bitrateTest = true;
          }
          // set new level to playlist loader : this will trigger start level load
          // hls.nextLoadLevel remains until it is set to a new value or until a new frag is successfully loaded
          this.level = hls.nextLoadLevel = startLevel;
          this.loadedmetadata = false;
        }
        // if startPosition undefined but lastCurrentTime set, set startPosition to last currentTime
        if (lastCurrentTime > 0 && startPosition === -1) {
          _logger.logger.log('override startPosition with lastCurrentTime @' + lastCurrentTime.toFixed(3));
          startPosition = lastCurrentTime;
        }
        this.state = State.IDLE;
        this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition;
        this.tick();
      } else {
        this.forceStartLoad = true;
        this.state = State.STOPPED;
      }
    }
  }, {
    key: 'stopLoad',
    value: function stopLoad() {
      var frag = this.fragCurrent;
      if (frag) {
        if (frag.loader) {
          frag.loader.abort();
        }
        this.fragCurrent = null;
      }
      this.fragPrevious = null;
      if (this.demuxer) {
        this.demuxer.destroy();
        this.demuxer = null;
      }
      this.state = State.STOPPED;
      this.forceStartLoad = false;
    }
  }, {
    key: 'tick',
    value: function tick() {
      this.ticks++;
      if (this.ticks === 1) {
        this.doTick();
        if (this.ticks > 1) {
          setTimeout(this.tick, 1);
        }
        this.ticks = 0;
      }
    }
  }, {
    key: 'doTick',
    value: function doTick() {
      switch (this.state) {
        case State.ERROR:
          //don't do anything in error state to avoid breaking further ...
          break;
        case State.BUFFER_FLUSHING:
          // in buffer flushing state, reset fragLoadError counter
          this.fragLoadError = 0;
          break;
        case State.IDLE:
          this._doTickIdle();
          break;
        case State.WAITING_LEVEL:
          var level = this.levels[this.level];
          // check if playlist is already loaded
          if (level && level.details) {
            this.state = State.IDLE;
          }
          break;
        case State.FRAG_LOADING_WAITING_RETRY:
          var now = performance.now();
          var retryDate = this.retryDate;
          // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading
          if (!retryDate || now >= retryDate || this.media && this.media.seeking) {
            _logger.logger.log('mediaController: retryDate reached, switch back to IDLE state');
            this.state = State.IDLE;
          }
          break;
        case State.ERROR:
        case State.STOPPED:
        case State.FRAG_LOADING:
        case State.PARSING:
        case State.PARSED:
        case State.ENDED:
          break;
        default:
          break;
      }
      // check buffer
      this._checkBuffer();
      // check/update current fragment
      this._checkFragmentChanged();
    }

    // Ironically the "idle" state is the on we do the most logic in it seems ....
    // NOTE: Maybe we could rather schedule a check for buffer length after half of the currently
    //       played segment, or on pause/play/seek instead of naively checking every 100ms?

  }, {
    key: '_doTickIdle',
    value: function _doTickIdle() {
      var hls = this.hls,
          config = hls.config,
          media = this.media;

      // if video not attached AND
      // start fragment already requested OR start frag prefetch disable
      // exit loop
      // => if start level loaded and media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop
      if (this.levelLastLoaded !== undefined && !media && (this.startFragRequested || !config.startFragPrefetch)) {
        return;
      }

      // if we have not yet loaded any fragment, start loading from start position
      var pos = void 0;
      if (this.loadedmetadata) {
        pos = media.currentTime;
      } else {
        pos = this.nextLoadPosition;
      }
      // determine next load level
      var level = hls.nextLoadLevel,
          levelInfo = this.levels[level];

      if (!levelInfo) {
        return;
      }

      var levelBitrate = levelInfo.bitrate,
          maxBufLen = void 0;

      // compute max Buffer Length that we could get from this load level, based on level bitrate. don't buffer more than 60 MB and more than 30s
      if (levelBitrate) {
        maxBufLen = Math.max(8 * config.maxBufferSize / levelBitrate, config.maxBufferLength);
      } else {
        maxBufLen = config.maxBufferLength;
      }
      maxBufLen = Math.min(maxBufLen, config.maxMaxBufferLength);

      // determine next candidate fragment to be loaded, based on current position and end of buffer position
      // ensure up to `config.maxMaxBufferLength` of buffer upfront

      var bufferInfo = _bufferHelper2.default.bufferInfo(this.mediaBuffer ? this.mediaBuffer : media, pos, config.maxBufferHole),
          bufferLen = bufferInfo.len;
      // Stay idle if we are still with buffer margins
      if (bufferLen >= maxBufLen) {
        return;
      }

      // if buffer length is less than maxBufLen try to load a new fragment ...
      _logger.logger.trace('buffer length of ' + bufferLen.toFixed(3) + ' is below max of ' + maxBufLen.toFixed(3) + '. checking for more payload ...');

      // set next load level : this will trigger a playlist load if needed
      this.level = hls.nextLoadLevel = level;

      var levelDetails = levelInfo.details;
      // if level info not retrieved yet, switch state and wait for level retrieval
      // if live playlist, ensure that new playlist has been refreshed to avoid loading/try to load
      // a useless and outdated fragment (that might even introduce load error if it is already out of the live playlist)
      if (typeof levelDetails === 'undefined' || levelDetails.live && this.levelLastLoaded !== level) {
        this.state = State.WAITING_LEVEL;
        return;
      }

      // we just got done loading the final fragment, and currentPos is buffered, and there is no other buffered range after ...
      // rationale is that in case there are any buffered rangesafter, it means that there are unbuffered portion in between
      // so we should not switch to ENDED in that case, to be able to buffer themx
      var fragPrevious = this.fragPrevious;
      if (!levelDetails.live && fragPrevious && fragPrevious.sn === levelDetails.endSN && bufferLen && !bufferInfo.nextStart) {
        // fragPrevious is last fragment. retrieve level duration using last frag start offset + duration
        // real duration might be lower than initial duration if there are drifts between real frag duration and playlist signaling
        var duration = Math.min(media.duration, fragPrevious.start + fragPrevious.duration);
        // if everything (almost) til the end is buffered, let's signal eos
        // we don't compare exactly media.duration === bufferInfo.end as there could be some subtle media duration difference (audio/video offsets...)
        // tolerate up to one frag duration to cope with these cases.
        // also cope with almost zero last frag duration (max last frag duration with 200ms) refer to https://github.com/video-dev/hls.js/pull/657
        if (duration - Math.max(bufferInfo.end, fragPrevious.start) <= Math.max(0.2, fragPrevious.duration)) {
          // Finalize the media stream
          var data = {};
          if (this.altAudio) {
            data.type = 'video';
          }
          this.hls.trigger(_events2.default.BUFFER_EOS, data);
          this.state = State.ENDED;
          return;
        }
      }

      // if we have the levelDetails for the selected variant, lets continue enrichen our stream (load keys/fragments or trigger EOS, etc..)
      this._fetchPayloadOrEos(pos, bufferInfo, levelDetails);
    }
  }, {
    key: '_fetchPayloadOrEos',
    value: function _fetchPayloadOrEos(pos, bufferInfo, levelDetails) {
      var fragPrevious = this.fragPrevious,
          level = this.level,
          fragments = levelDetails.fragments,
          fragLen = fragments.length;

      // empty playlist
      if (fragLen === 0) {
        return;
      }

      // find fragment index, contiguous with end of buffer position
      var start = fragments[0].start,
          end = fragments[fragLen - 1].start + fragments[fragLen - 1].duration,
          bufferEnd = bufferInfo.end,
          frag = void 0;

      if (levelDetails.initSegment && !levelDetails.initSegment.data) {
        frag = levelDetails.initSegment;
      } else {
        // in case of live playlist we need to ensure that requested position is not located before playlist start
        if (levelDetails.live) {
          var initialLiveManifestSize = this.config.initialLiveManifestSize;
          if (fragLen < initialLiveManifestSize) {
            _logger.logger.warn('Can not start playback of a level, reason: not enough fragments ' + fragLen + ' < ' + initialLiveManifestSize);
            return;
          }

          frag = this._ensureFragmentAtLivePoint(levelDetails, bufferEnd, start, end, fragPrevious, fragments, fragLen);
          // if it explicitely returns null don't load any fragment and exit function now
          if (frag === null) {
            return;
          }
        } else {
          // VoD playlist: if bufferEnd before start of playlist, load first fragment
          if (bufferEnd < start) {
            frag = fragments[0];
          }
        }
      }
      if (!frag) {
        frag = this._findFragment(start, fragPrevious, fragLen, fragments, bufferEnd, end, levelDetails);
      }
      if (frag) {
        this._loadFragmentOrKey(frag, level, levelDetails, pos, bufferEnd);
      }
      return;
    }
  }, {
    key: '_ensureFragmentAtLivePoint',
    value: function _ensureFragmentAtLivePoint(levelDetails, bufferEnd, start, end, fragPrevious, fragments, fragLen) {
      var config = this.hls.config,
          media = this.media;

      var frag = void 0;

      // check if requested position is within seekable boundaries :
      //logger.log(`start/pos/bufEnd/seeking:${start.toFixed(3)}/${pos.toFixed(3)}/${bufferEnd.toFixed(3)}/${this.media.seeking}`);
      var maxLatency = config.liveMaxLatencyDuration !== undefined ? config.liveMaxLatencyDuration : config.liveMaxLatencyDurationCount * levelDetails.targetduration;

      if (bufferEnd < Math.max(start - config.maxFragLookUpTolerance, end - maxLatency)) {
        var liveSyncPosition = this.liveSyncPosition = this.computeLivePosition(start, levelDetails);
        _logger.logger.log('buffer end: ' + bufferEnd.toFixed(3) + ' is located too far from the end of live sliding playlist, reset currentTime to : ' + liveSyncPosition.toFixed(3));
        bufferEnd = liveSyncPosition;
        if (media && media.readyState && media.duration > liveSyncPosition) {
          media.currentTime = liveSyncPosition;
        }
        this.nextLoadPosition = liveSyncPosition;
      }

      // if end of buffer greater than live edge, don't load any fragment
      // this could happen if live playlist intermittently slides in the past.
      // level 1 loaded [182580161,182580167]
      // level 1 loaded [182580162,182580169]
      // Loading 182580168 of [182580162 ,182580169],level 1 ..
      // Loading 182580169 of [182580162 ,182580169],level 1 ..
      // level 1 loaded [182580162,182580168] <============= here we should have bufferEnd > end. in that case break to avoid reloading 182580168
      // level 1 loaded [182580164,182580171]
      //
      // don't return null in case media not loaded yet (readystate === 0)
      if (levelDetails.PTSKnown && bufferEnd > end && media && media.readyState) {
        return null;
      }

      if (this.startFragRequested && !levelDetails.PTSKnown) {
        /* we are switching level on live playlist, but we don't have any PTS info for that quality level ...
           try to load frag matching with next SN.
           even if SN are not synchronized between playlists, loading this frag will help us
           compute playlist sliding and find the right one after in case it was not the right consecutive one */
        if (fragPrevious) {
          var targetSN = fragPrevious.sn + 1;
          if (targetSN >= levelDetails.startSN && targetSN <= levelDetails.endSN) {
            var fragNext = fragments[targetSN - levelDetails.startSN];
            if (fragPrevious.cc === fragNext.cc) {
              frag = fragNext;
              _logger.logger.log('live playlist, switching playlist, load frag with next SN: ' + frag.sn);
            }
          }
          // next frag SN not available (or not with same continuity counter)
          // look for a frag sharing the same CC
          if (!frag) {
            frag = _binarySearch2.default.search(fragments, function (frag) {
              return fragPrevious.cc - frag.cc;
            });
            if (frag) {
              _logger.logger.log('live playlist, switching playlist, load frag with same CC: ' + frag.sn);
            }
          }
        }
        if (!frag) {
          /* we have no idea about which fragment should be loaded.
             so let's load mid fragment. it will help computing playlist sliding and find the right one
          */
          frag = fragments[Math.min(fragLen - 1, Math.round(fragLen / 2))];
          _logger.logger.log('live playlist, switching playlist, unknown, load middle frag : ' + frag.sn);
        }
      }
      return frag;
    }
  }, {
    key: '_findFragment',
    value: function _findFragment(start, fragPrevious, fragLen, fragments, bufferEnd, end, levelDetails) {
      var config = this.hls.config;
      var frag = void 0;
      var foundFrag = void 0;
      var maxFragLookUpTolerance = config.maxFragLookUpTolerance;
      var fragNext = fragPrevious ? fragments[fragPrevious.sn - fragments[0].sn + 1] : undefined;
      var fragmentWithinToleranceTest = function fragmentWithinToleranceTest(candidate) {
        // offset should be within fragment boundary - config.maxFragLookUpTolerance
        // this is to cope with situations like
        // bufferEnd = 9.991
        // frag[Ø] : [0,10]
        // frag[1] : [10,20]
        // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here
        //              frag start               frag start+duration
        //                  |-----------------------------|
        //              <--->                         <--->
        //  ...--------><-----------------------------><---------....
        // previous frag         matching fragment         next frag
        //  return -1             return 0                 return 1
        //logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`);
        // Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments
        var candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration);
        if (candidate.start + candidate.duration - candidateLookupTolerance <= bufferEnd) {
          return 1;
        } // if maxFragLookUpTolerance will have negative value then don't return -1 for first element
        else if (candidate.start - candidateLookupTolerance > bufferEnd && candidate.start) {
            return -1;
          }
        return 0;
      };

      if (bufferEnd < end) {
        if (bufferEnd > end - maxFragLookUpTolerance) {
          maxFragLookUpTolerance = 0;
        }
        // Prefer the next fragment if it's within tolerance
        if (fragNext && !fragmentWithinToleranceTest(fragNext)) {
          foundFrag = fragNext;
        } else {
          foundFrag = _binarySearch2.default.search(fragments, fragmentWithinToleranceTest);
        }
      } else {
        // reach end of playlist
        foundFrag = fragments[fragLen - 1];
      }
      if (foundFrag) {
        frag = foundFrag;
        var curSNIdx = frag.sn - levelDetails.startSN;
        var sameLevel = fragPrevious && frag.level === fragPrevious.level;
        var prevFrag = fragments[curSNIdx - 1];
        var nextFrag = fragments[curSNIdx + 1];
        //logger.log('find SN matching with pos:' +  bufferEnd + ':' + frag.sn);
        if (fragPrevious && frag.sn === fragPrevious.sn) {
          if (sameLevel && !frag.backtracked) {
            if (frag.sn < levelDetails.endSN) {
              var deltaPTS = fragPrevious.deltaPTS;
              // if there is a significant delta between audio and video, larger than max allowed hole,
              // and if previous remuxed fragment did not start with a keyframe. (fragPrevious.dropped)
              // let's try to load previous fragment again to get last keyframe
              // then we will reload again current fragment (that way we should be able to fill the buffer hole ...)
              if (deltaPTS && deltaPTS > config.maxBufferHole && fragPrevious.dropped && curSNIdx) {
                frag = prevFrag;
                _logger.logger.warn('SN just loaded, with large PTS gap between audio and video, maybe frag is not starting with a keyframe ? load previous one to try to overcome this');
                // decrement previous frag load counter to avoid frag loop loading error when next fragment will get reloaded
                fragPrevious.loadCounter--;
              } else {
                frag = nextFrag;
                _logger.logger.log('SN just loaded, load next one: ' + frag.sn);
              }
            } else {
              frag = null;
            }
          } else if (frag.backtracked) {
            // Only backtrack a max of 1 consecutive fragment to prevent sliding back too far when little or no frags start with keyframes
            if (nextFrag && nextFrag.backtracked) {
              _logger.logger.warn('Already backtracked from fragment ' + nextFrag.sn + ', will not backtrack to fragment ' + frag.sn + '. Loading fragment ' + nextFrag.sn);
              frag = nextFrag;
            } else {
              // If a fragment has dropped frames and it's in a same level/sequence, load the previous fragment to try and find the keyframe
              // Reset the dropped count now since it won't be reset until we parse the fragment again, which prevents infinite backtracking on the same segment
              _logger.logger.warn('Loaded fragment with dropped frames, backtracking 1 segment to find a keyframe');
              frag.dropped = 0;
              if (prevFrag) {
                if (prevFrag.loadCounter) {
                  prevFrag.loadCounter--;
                }
                frag = prevFrag;
                frag.backtracked = true;
              } else if (curSNIdx) {
                // can't backtrack on very first fragment
                frag = null;
              }
            }
          }
        }
      }
      return frag;
    }
  }, {
    key: '_loadFragmentOrKey',
    value: function _loadFragmentOrKey(frag, level, levelDetails, pos, bufferEnd) {
      var hls = this.hls,
          config = hls.config;

      //logger.log('loading frag ' + i +',pos/bufEnd:' + pos.toFixed(3) + '/' + bufferEnd.toFixed(3));
      if (frag.decryptdata && frag.decryptdata.uri != null && frag.decryptdata.key == null) {
        _logger.logger.log('Loading key for ' + frag.sn + ' of [' + levelDetails.startSN + ' ,' + levelDetails.endSN + '],level ' + level);
        this.state = State.KEY_LOADING;
        hls.trigger(_events2.default.KEY_LOADING, { frag: frag });
      } else {
        _logger.logger.log('Loading ' + frag.sn + ' of [' + levelDetails.startSN + ' ,' + levelDetails.endSN + '],level ' + level + ', currentTime:' + pos.toFixed(3) + ',bufferEnd:' + bufferEnd.toFixed(3));
        // ensure that we are not reloading the same fragments in loop ...
        if (this.fragLoadIdx !== undefined) {
          this.fragLoadIdx++;
        } else {
          this.fragLoadIdx = 0;
        }
        if (frag.loadCounter) {
          frag.loadCounter++;
          var maxThreshold = config.fragLoadingLoopThreshold;
          // if this frag has already been loaded 3 times, and if it has been reloaded recently
          if (frag.loadCounter > maxThreshold && Math.abs(this.fragLoadIdx - frag.loadIdx) < maxThreshold) {
            hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.FRAG_LOOP_LOADING_ERROR, fatal: false, frag: frag });
            return;
          }
        } else {
          frag.loadCounter = 1;
        }
        frag.loadIdx = this.fragLoadIdx;
        this.fragCurrent = frag;
        this.startFragRequested = true;
        if (!isNaN(frag.sn)) {
          this.nextLoadPosition = frag.start + frag.duration;
        }
        frag.autoLevel = hls.autoLevelEnabled;
        frag.bitrateTest = this.bitrateTest;
        hls.trigger(_events2.default.FRAG_LOADING, { frag: frag });
        // lazy demuxer init, as this could take some time ... do it during frag loading
        if (!this.demuxer) {
          this.demuxer = new _demuxer2.default(hls, 'main');
        }
        this.state = State.FRAG_LOADING;
        return;
      }
    }
  }, {
    key: 'getBufferedFrag',
    value: function getBufferedFrag(position) {
      return _binarySearch2.default.search(this._bufferedFrags, function (frag) {
        if (position < frag.startPTS) {
          return -1;
        } else if (position > frag.endPTS) {
          return 1;
        }
        return 0;
      });
    }
  }, {
    key: 'followingBufferedFrag',
    value: function followingBufferedFrag(frag) {
      if (frag) {
        // try to get range of next fragment (500ms after this range)
        return this.getBufferedFrag(frag.endPTS + 0.5);
      }
      return null;
    }
  }, {
    key: '_checkFragmentChanged',
    value: function _checkFragmentChanged() {
      var fragPlayingCurrent,
          currentTime,
          video = this.media;
      if (video && video.readyState && video.seeking === false) {
        currentTime = video.currentTime;
        /* if video element is in seeked state, currentTime can only increase.
          (assuming that playback rate is positive ...)
          As sometimes currentTime jumps back to zero after a
          media decode error, check this, to avoid seeking back to
          wrong position after a media decode error
        */
        if (currentTime > video.playbackRate * this.lastCurrentTime) {
          this.lastCurrentTime = currentTime;
        }
        if (_bufferHelper2.default.isBuffered(video, currentTime)) {
          fragPlayingCurrent = this.getBufferedFrag(currentTime);
        } else if (_bufferHelper2.default.isBuffered(video, currentTime + 0.1)) {
          /* ensure that FRAG_CHANGED event is triggered at startup,
            when first video frame is displayed and playback is paused.
            add a tolerance of 100ms, in case current position is not buffered,
            check if current pos+100ms is buffered and use that buffer range
            for FRAG_CHANGED event reporting */
          fragPlayingCurrent = this.getBufferedFrag(currentTime + 0.1);
        }
        if (fragPlayingCurrent) {
          var fragPlaying = fragPlayingCurrent;
          if (fragPlaying !== this.fragPlaying) {
            this.hls.trigger(_events2.default.FRAG_CHANGED, { frag: fragPlaying });
            var fragPlayingLevel = fragPlaying.level;
            if (!this.fragPlaying || this.fragPlaying.level !== fragPlayingLevel) {
              this.hls.trigger(_events2.default.LEVEL_SWITCHED, { level: fragPlayingLevel });
            }
            this.fragPlaying = fragPlaying;
          }
        }
      }
    }

    /*
      on immediate level switch :
       - pause playback if playing
       - cancel any pending load request
       - and trigger a buffer flush
    */

  }, {
    key: 'immediateLevelSwitch',
    value: function immediateLevelSwitch() {
      _logger.logger.log('immediateLevelSwitch');
      if (!this.immediateSwitch) {
        this.immediateSwitch = true;
        var media = this.media,
            previouslyPaused = void 0;
        if (media) {
          previouslyPaused = media.paused;
          media.pause();
        } else {
          // don't restart playback after instant level switch in case media not attached
          previouslyPaused = true;
        }
        this.previouslyPaused = previouslyPaused;
      }
      var fragCurrent = this.fragCurrent;
      if (fragCurrent && fragCurrent.loader) {
        fragCurrent.loader.abort();
      }
      this.fragCurrent = null;
      // increase fragment load Index to avoid frag loop loading error after buffer flush
      this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold;
      // flush everything
      this.flushMainBuffer(0, Number.POSITIVE_INFINITY);
    }

    /*
       on immediate level switch end, after new fragment has been buffered :
        - nudge video decoder by slightly adjusting video currentTime (if currentTime buffered)
        - resume the playback if needed
    */

  }, {
    key: 'immediateLevelSwitchEnd',
    value: function immediateLevelSwitchEnd() {
      var media = this.media;
      if (media && media.buffered.length) {
        this.immediateSwitch = false;
        if (_bufferHelper2.default.isBuffered(media, media.currentTime)) {
          // only nudge if currentTime is buffered
          media.currentTime -= 0.0001;
        }
        if (!this.previouslyPaused) {
          media.play();
        }
      }
    }
  }, {
    key: 'nextLevelSwitch',
    value: function nextLevelSwitch() {
      /* try to switch ASAP without breaking video playback :
         in order to ensure smooth but quick level switching,
        we need to find the next flushable buffer range
        we should take into account new segment fetch time
      */
      var media = this.media;
      // ensure that media is defined and that metadata are available (to retrieve currentTime)
      if (media && media.readyState) {
        var fetchdelay = void 0,
            fragPlayingCurrent = void 0,
            nextBufferedFrag = void 0;
        // increase fragment load Index to avoid frag loop loading error after buffer flush
        this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold;
        fragPlayingCurrent = this.getBufferedFrag(media.currentTime);
        if (fragPlayingCurrent && fragPlayingCurrent.startPTS > 1) {
          // flush buffer preceding current fragment (flush until current fragment start offset)
          // minus 1s to avoid video freezing, that could happen if we flush keyframe of current video ...
          this.flushMainBuffer(0, fragPlayingCurrent.startPTS - 1);
        }
        if (!media.paused) {
          // add a safety delay of 1s
          var nextLevelId = this.hls.nextLoadLevel,
              nextLevel = this.levels[nextLevelId],
              fragLastKbps = this.fragLastKbps;
          if (fragLastKbps && this.fragCurrent) {
            fetchdelay = this.fragCurrent.duration * nextLevel.bitrate / (1000 * fragLastKbps) + 1;
          } else {
            fetchdelay = 0;
          }
        } else {
          fetchdelay = 0;
        }
        //logger.log('fetchdelay:'+fetchdelay);
        // find buffer range that will be reached once new fragment will be fetched
        nextBufferedFrag = this.getBufferedFrag(media.currentTime + fetchdelay);
        if (nextBufferedFrag) {
          // we can flush buffer range following this one without stalling playback
          nextBufferedFrag = this.followingBufferedFrag(nextBufferedFrag);
          if (nextBufferedFrag) {
            // if we are here, we can also cancel any loading/demuxing in progress, as they are useless
            var fragCurrent = this.fragCurrent;
            if (fragCurrent && fragCurrent.loader) {
              fragCurrent.loader.abort();
            }
            this.fragCurrent = null;
            // start flush position is the start PTS of next buffered frag.
            // we use frag.naxStartPTS which is max(audio startPTS, video startPTS).
            // in case there is a small PTS Delta between audio and video, using maxStartPTS avoids flushing last samples from current fragment
            this.flushMainBuffer(nextBufferedFrag.maxStartPTS, Number.POSITIVE_INFINITY);
          }
        }
      }
    }
  }, {
    key: 'flushMainBuffer',
    value: function flushMainBuffer(startOffset, endOffset) {
      this.state = State.BUFFER_FLUSHING;
      var flushScope = { startOffset: startOffset, endOffset: endOffset };
      // if alternate audio tracks are used, only flush video, otherwise flush everything
      if (this.altAudio) {
        flushScope.type = 'video';
      }
      this.hls.trigger(_events2.default.BUFFER_FLUSHING, flushScope);
    }
  }, {
    key: 'onMediaAttached',
    value: function onMediaAttached(data) {
      var media = this.media = this.mediaBuffer = data.media;
      this.onvseeking = this.onMediaSeeking.bind(this);
      this.onvseeked = this.onMediaSeeked.bind(this);
      this.onvended = this.onMediaEnded.bind(this);
      media.addEventListener('seeking', this.onvseeking);
      media.addEventListener('seeked', this.onvseeked);
      media.addEventListener('ended', this.onvended);
      var config = this.config;
      if (this.levels && config.autoStartLoad) {
        this.hls.startLoad(config.startPosition);
      }
    }
  }, {
    key: 'onMediaDetaching',
    value: function onMediaDetaching() {
      var media = this.media;
      if (media && media.ended) {
        _logger.logger.log('MSE detaching and video ended, reset startPosition');
        this.startPosition = this.lastCurrentTime = 0;
      }

      // reset fragment loading counter on MSE detaching to avoid reporting FRAG_LOOP_LOADING_ERROR after error recovery
      var levels = this.levels;
      if (levels) {
        // reset fragment load counter
        levels.forEach(function (level) {
          if (level.details) {
            level.details.fragments.forEach(function (fragment) {
              fragment.loadCounter = undefined;
              fragment.backtracked = undefined;
            });
          }
        });
      }
      // remove video listeners
      if (media) {
        media.removeEventListener('seeking', this.onvseeking);
        media.removeEventListener('seeked', this.onvseeked);
        media.removeEventListener('ended', this.onvended);
        this.onvseeking = this.onvseeked = this.onvended = null;
      }
      this.media = this.mediaBuffer = null;
      this.loadedmetadata = false;
      this.stopLoad();
    }
  }, {
    key: 'onMediaSeeking',
    value: function onMediaSeeking() {
      var media = this.media,
          currentTime = media ? media.currentTime : undefined,
          config = this.config;
      if (!isNaN(currentTime)) {
        _logger.logger.log('media seeking to ' + currentTime.toFixed(3));
      }
      var mediaBuffer = this.mediaBuffer ? this.mediaBuffer : media;
      var bufferInfo = _bufferHelper2.default.bufferInfo(mediaBuffer, currentTime, this.config.maxBufferHole);
      if (this.state === State.FRAG_LOADING) {
        var fragCurrent = this.fragCurrent;
        // check if we are seeking to a unbuffered area AND if frag loading is in progress
        if (bufferInfo.len === 0 && fragCurrent) {
          var tolerance = config.maxFragLookUpTolerance,
              fragStartOffset = fragCurrent.start - tolerance,
              fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
          // check if we seek position will be out of currently loaded frag range : if out cancel frag load, if in, don't do anything
          if (currentTime < fragStartOffset || currentTime > fragEndOffset) {
            if (fragCurrent.loader) {
              _logger.logger.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
              fragCurrent.loader.abort();
            }
            this.fragCurrent = null;
            this.fragPrevious = null;
            // switch to IDLE state to load new fragment
            this.state = State.IDLE;
          } else {
            _logger.logger.log('seeking outside of buffer but within currently loaded fragment range');
          }
        }
      } else if (this.state === State.ENDED) {
        // if seeking to unbuffered area, clean up fragPrevious
        if (bufferInfo.len === 0) {
          this.fragPrevious = 0;
        }
        // switch to IDLE state to check for potential new fragment
        this.state = State.IDLE;
      }
      if (media) {
        this.lastCurrentTime = currentTime;
      }
      // avoid reporting fragment loop loading error in case user is seeking several times on same position
      if (this.state !== State.FRAG_LOADING && this.fragLoadIdx !== undefined) {
        this.fragLoadIdx += 2 * config.fragLoadingLoopThreshold;
      }
      // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
      if (!this.loadedmetadata) {
        this.nextLoadPosition = this.startPosition = currentTime;
      }
      // tick to speed up processing
      this.tick();
    }
  }, {
    key: 'onMediaSeeked',
    value: function onMediaSeeked() {
      var media = this.media,
          currentTime = media ? media.currentTime : undefined;
      if (!isNaN(currentTime)) {
        _logger.logger.log('media seeked to ' + currentTime.toFixed(3));
      }
      // tick to speed up FRAGMENT_PLAYING triggering
      this.tick();
    }
  }, {
    key: 'onMediaEnded',
    value: function onMediaEnded() {
      _logger.logger.log('media ended');
      // reset startPosition and lastCurrentTime to restart playback @ stream beginning
      this.startPosition = this.lastCurrentTime = 0;
    }
  }, {
    key: 'onManifestLoading',
    value: function onManifestLoading() {
      // reset buffer on manifest loading
      _logger.logger.log('trigger BUFFER_RESET');
      this.hls.trigger(_events2.default.BUFFER_RESET);
      this._bufferedFrags = [];
      this.stalled = false;
      this.startPosition = this.lastCurrentTime = 0;
    }
  }, {
    key: 'onManifestParsed',
    value: function onManifestParsed(data) {
      var aac = false,
          heaac = false,
          codec;
      data.levels.forEach(function (level) {
        // detect if we have different kind of audio codecs used amongst playlists
        codec = level.audioCodec;
        if (codec) {
          if (codec.indexOf('mp4a.40.2') !== -1) {
            aac = true;
          }
          if (codec.indexOf('mp4a.40.5') !== -1) {
            heaac = true;
          }
        }
      });
      this.audioCodecSwitch = aac && heaac;
      if (this.audioCodecSwitch) {
        _logger.logger.log('both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC');
      }
      this.levels = data.levels;
      this.startLevelLoaded = false;
      this.startFragRequested = false;
      var config = this.config;
      if (config.autoStartLoad || this.forceStartLoad) {
        this.hls.startLoad(config.startPosition);
      }
    }
  }, {
    key: 'onLevelLoaded',
    value: function onLevelLoaded(data) {
      var newDetails = data.details,
          newLevelId = data.level,
          curLevel = this.levels[newLevelId],
          duration = newDetails.totalduration,
          sliding = 0;

      _logger.logger.log('level ' + newLevelId + ' loaded [' + newDetails.startSN + ',' + newDetails.endSN + '],duration:' + duration);
      this.levelLastLoaded = newLevelId;

      if (newDetails.live) {
        var curDetails = curLevel.details;
        if (curDetails && newDetails.fragments.length > 0) {
          // we already have details for that level, merge them
          _levelHelper2.default.mergeDetails(curDetails, newDetails);
          sliding = newDetails.fragments[0].start;
          this.liveSyncPosition = this.computeLivePosition(sliding, curDetails);
          if (newDetails.PTSKnown) {
            _logger.logger.log('live playlist sliding:' + sliding.toFixed(3));
          } else {
            _logger.logger.log('live playlist - outdated PTS, unknown sliding');
          }
        } else {
          newDetails.PTSKnown = false;
          _logger.logger.log('live playlist - first load, unknown sliding');
        }
      } else {
        newDetails.PTSKnown = false;
      }
      // override level info
      curLevel.details = newDetails;
      this.hls.trigger(_events2.default.LEVEL_UPDATED, { details: newDetails, level: newLevelId });

      if (this.startFragRequested === false) {
        // compute start position if set to -1. use it straight away if value is defined
        if (this.startPosition === -1 || this.lastCurrentTime === -1) {
          // first, check if start time offset has been set in playlist, if yes, use this value
          var startTimeOffset = newDetails.startTimeOffset;
          if (!isNaN(startTimeOffset)) {
            if (startTimeOffset < 0) {
              _logger.logger.log('negative start time offset ' + startTimeOffset + ', count from end of last fragment');
              startTimeOffset = sliding + duration + startTimeOffset;
            }
            _logger.logger.log('start time offset found in playlist, adjust startPosition to ' + startTimeOffset);
            this.startPosition = startTimeOffset;
          } else {
            // if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3)
            if (newDetails.live) {
              this.startPosition = this.computeLivePosition(sliding, newDetails);
              _logger.logger.log('configure startPosition to ' + this.startPosition);
            } else {
              this.startPosition = 0;
            }
          }
          this.lastCurrentTime = this.startPosition;
        }
        this.nextLoadPosition = this.startPosition;
      }
      // only switch batck to IDLE state if we were waiting for level to start downloading a new fragment
      if (this.state === State.WAITING_LEVEL) {
        this.state = State.IDLE;
      }
      //trigger handler right now
      this.tick();
    }
  }, {
    key: 'onKeyLoaded',
    value: function onKeyLoaded() {
      if (this.state === State.KEY_LOADING) {
        this.state = State.IDLE;
        this.tick();
      }
    }
  }, {
    key: 'onFragLoaded',
    value: function onFragLoaded(data) {
      var fragCurrent = this.fragCurrent,
          fragLoaded = data.frag;
      if (this.state === State.FRAG_LOADING && fragCurrent && fragLoaded.type === 'main' && fragLoaded.level === fragCurrent.level && fragLoaded.sn === fragCurrent.sn) {
        var stats = data.stats,
            currentLevel = this.levels[fragCurrent.level],
            details = currentLevel.details;
        _logger.logger.log('Loaded  ' + fragCurrent.sn + ' of [' + details.startSN + ' ,' + details.endSN + '],level ' + fragCurrent.level);
        // reset frag bitrate test in any case after frag loaded event
        this.bitrateTest = false;
        this.stats = stats;
        // if this frag was loaded to perform a bitrate test AND if hls.nextLoadLevel is greater than 0
        // then this means that we should be able to load a fragment at a higher quality level
        if (fragLoaded.bitrateTest === true && this.hls.nextLoadLevel) {
          // switch back to IDLE state ... we just loaded a fragment to determine adequate start bitrate and initialize autoswitch algo
          this.state = State.IDLE;
          this.startFragRequested = false;
          stats.tparsed = stats.tbuffered = performance.now();
          this.hls.trigger(_events2.default.FRAG_BUFFERED, { stats: stats, frag: fragCurrent, id: 'main' });
          this.tick();
        } else if (fragLoaded.sn === 'initSegment') {
          this.state = State.IDLE;
          stats.tparsed = stats.tbuffered = performance.now();
          details.initSegment.data = data.payload;
          this.hls.trigger(_events2.default.FRAG_BUFFERED, { stats: stats, frag: fragCurrent, id: 'main' });
          this.tick();
        } else {
          this.state = State.PARSING;
          // transmux the MPEG-TS data to ISO-BMFF segments
          var duration = details.totalduration,
              level = fragCurrent.level,
              sn = fragCurrent.sn,
              audioCodec = this.config.defaultAudioCodec || currentLevel.audioCodec;
          if (this.audioCodecSwap) {
            _logger.logger.log('swapping playlist audio codec');
            if (audioCodec === undefined) {
              audioCodec = this.lastAudioCodec;
            }
            if (audioCodec) {
              if (audioCodec.indexOf('mp4a.40.5') !== -1) {
                audioCodec = 'mp4a.40.2';
              } else {
                audioCodec = 'mp4a.40.5';
              }
            }
          }
          this.pendingBuffering = true;
          this.appended = false;
          _logger.logger.log('Parsing ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],level ' + level + ', cc ' + fragCurrent.cc);
          var demuxer = this.demuxer;
          if (!demuxer) {
            demuxer = this.demuxer = new _demuxer2.default(this.hls, 'main');
          }
          // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live) and if media is not seeking (this is to overcome potential timestamp drifts between playlists and fragments)
          var media = this.media;
          var mediaSeeking = media && media.seeking;
          var accurateTimeOffset = !mediaSeeking && (details.PTSKnown || !details.live);
          var initSegmentData = details.initSegment ? details.initSegment.data : [];
          demuxer.push(data.payload, initSegmentData, audioCodec, currentLevel.videoCodec, fragCurrent, duration, accurateTimeOffset, undefined);
        }
      }
      this.fragLoadError = 0;
    }
  }, {
    key: 'onFragParsingInitSegment',
    value: function onFragParsingInitSegment(data) {
      var fragCurrent = this.fragCurrent;
      var fragNew = data.frag;
      if (fragCurrent && data.id === 'main' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === State.PARSING) {
        var tracks = data.tracks,
            trackName,
            track;

        // if audio track is expected to come from audio stream controller, discard any coming from main
        if (tracks.audio && this.altAudio) {
          delete tracks.audio;
        }
        // include levelCodec in audio and video tracks
        track = tracks.audio;
        if (track) {
          var audioCodec = this.levels[this.level].audioCodec,
              ua = navigator.userAgent.toLowerCase();
          if (audioCodec && this.audioCodecSwap) {
            _logger.logger.log('swapping playlist audio codec');
            if (audioCodec.indexOf('mp4a.40.5') !== -1) {
              audioCodec = 'mp4a.40.2';
            } else {
              audioCodec = 'mp4a.40.5';
            }
          }
          // in case AAC and HE-AAC audio codecs are signalled in manifest
          // force HE-AAC , as it seems that most browsers prefers that way,
          // except for mono streams OR on FF
          // these conditions might need to be reviewed ...
          if (this.audioCodecSwitch) {
            // don't force HE-AAC if mono stream
            if (track.metadata.channelCount !== 1 &&
            // don't force HE-AAC if firefox
            ua.indexOf('firefox') === -1) {
              audioCodec = 'mp4a.40.5';
            }
          }
          // HE-AAC is broken on Android, always signal audio codec as AAC even if variant manifest states otherwise
          if (ua.indexOf('android') !== -1 && track.container !== 'audio/mpeg') {
            // Exclude mpeg audio
            audioCodec = 'mp4a.40.2';
            _logger.logger.log('Android: force audio codec to ' + audioCodec);
          }
          track.levelCodec = audioCodec;
          track.id = data.id;
        }
        track = tracks.video;
        if (track) {
          track.levelCodec = this.levels[this.level].videoCodec;
          track.id = data.id;
        }
        this.hls.trigger(_events2.default.BUFFER_CODECS, tracks);
        // loop through tracks that are going to be provided to bufferController
        for (trackName in tracks) {
          track = tracks[trackName];
          _logger.logger.log('main track:' + trackName + ',container:' + track.container + ',codecs[level/parsed]=[' + track.levelCodec + '/' + track.codec + ']');
          var initSegment = track.initSegment;
          if (initSegment) {
            this.appended = true;
            // arm pending Buffering flag before appending a segment
            this.pendingBuffering = true;
            this.hls.trigger(_events2.default.BUFFER_APPENDING, { type: trackName, data: initSegment, parent: 'main', content: 'initSegment' });
          }
        }
        //trigger handler right now
        this.tick();
      }
    }
  }, {
    key: 'onFragParsingData',
    value: function onFragParsingData(data) {
      var _this2 = this;

      var fragCurrent = this.fragCurrent;
      var fragNew = data.frag;
      if (fragCurrent && data.id === 'main' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && !(data.type === 'audio' && this.altAudio) && // filter out main audio if audio track is loaded through audio stream controller
      this.state === State.PARSING) {
        var level = this.levels[this.level],
            frag = fragCurrent;
        if (isNaN(data.endPTS)) {
          data.endPTS = data.startPTS + fragCurrent.duration;
          data.endDTS = data.startDTS + fragCurrent.duration;
        }

        _logger.logger.log('Parsed ' + data.type + ',PTS:[' + data.startPTS.toFixed(3) + ',' + data.endPTS.toFixed(3) + '],DTS:[' + data.startDTS.toFixed(3) + '/' + data.endDTS.toFixed(3) + '],nb:' + data.nb + ',dropped:' + (data.dropped || 0));

        // Detect gaps in a fragment  and try to fix it by finding a keyframe in the previous fragment (see _findFragments)
        if (data.type === 'video') {
          frag.dropped = data.dropped;
          if (frag.dropped) {
            if (!frag.backtracked) {
              _logger.logger.warn('missing video frame(s), backtracking fragment');
              // Return back to the IDLE state without appending to buffer
              // Causes findFragments to backtrack a segment and find the keyframe
              // Audio fragments arriving before video sets the nextLoadPosition, causing _findFragments to skip the backtracked fragment
              frag.backtracked = true;
              this.nextLoadPosition = data.startPTS;
              this.state = State.IDLE;
              this.fragPrevious = frag;
              this.tick();
              return;
            } else {
              _logger.logger.warn('Already backtracked on this fragment, appending with the gap');
            }
          } else {
            // Only reset the backtracked flag if we've loaded the frag without any dropped frames
            frag.backtracked = false;
          }
        }

        var drift = _levelHelper2.default.updateFragPTSDTS(level.details, frag, data.startPTS, data.endPTS, data.startDTS, data.endDTS),
            hls = this.hls;
        hls.trigger(_events2.default.LEVEL_PTS_UPDATED, { details: level.details, level: this.level, drift: drift, type: data.type, start: data.startPTS, end: data.endPTS });

        // has remuxer dropped video frames located before first keyframe ?
        [data.data1, data.data2].forEach(function (buffer) {
          // only append in PARSING state (rationale is that an appending error could happen synchronously on first segment appending)
          // in that case it is useless to append following segments
          if (buffer && buffer.length && _this2.state === State.PARSING) {
            _this2.appended = true;
            // arm pending Buffering flag before appending a segment
            _this2.pendingBuffering = true;
            hls.trigger(_events2.default.BUFFER_APPENDING, { type: data.type, data: buffer, parent: 'main', content: 'data' });
          }
        });
        //trigger handler right now
        this.tick();
      }
    }
  }, {
    key: 'onFragParsed',
    value: function onFragParsed(data) {
      var fragCurrent = this.fragCurrent;
      var fragNew = data.frag;
      if (fragCurrent && data.id === 'main' && fragNew.sn === fragCurrent.sn && fragNew.level === fragCurrent.level && this.state === State.PARSING) {
        this.stats.tparsed = performance.now();
        this.state = State.PARSED;
        this._checkAppendedParsed();
      }
    }
  }, {
    key: 'onAudioTrackSwitching',
    value: function onAudioTrackSwitching(data) {
      // if any URL found on new audio track, it is an alternate audio track
      var altAudio = !!data.url,
          trackId = data.id;
      // if we switch on main audio, ensure that main fragment scheduling is synced with media.buffered
      // don't do anything if we switch to alt audio: audio stream controller is handling it.
      // we will just have to change buffer scheduling on audioTrackSwitched
      if (!altAudio) {
        if (this.mediaBuffer !== this.media) {
          _logger.logger.log('switching on main audio, use media.buffered to schedule main fragment loading');
          this.mediaBuffer = this.media;
          var fragCurrent = this.fragCurrent;
          // we need to refill audio buffer from main: cancel any frag loading to speed up audio switch
          if (fragCurrent.loader) {
            _logger.logger.log('switching to main audio track, cancel main fragment load');
            fragCurrent.loader.abort();
          }
          this.fragCurrent = null;
          this.fragPrevious = null;
          // destroy demuxer to force init segment generation (following audio switch)
          if (this.demuxer) {
            this.demuxer.destroy();
            this.demuxer = null;
          }
          // switch to IDLE state to load new fragment
          this.state = State.IDLE;
        }
        var hls = this.hls;
        // switching to main audio, flush all audio and trigger track switched
        hls.trigger(_events2.default.BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY, type: 'audio' });
        hls.trigger(_events2.default.AUDIO_TRACK_SWITCHED, { id: trackId });
        this.altAudio = false;
      }
    }
  }, {
    key: 'onAudioTrackSwitched',
    value: function onAudioTrackSwitched(data) {
      var trackId = data.id,
          altAudio = !!this.hls.audioTracks[trackId].url;
      if (altAudio) {
        var videoBuffer = this.videoBuffer;
        // if we switched on alternate audio, ensure that main fragment scheduling is synced with video sourcebuffer buffered
        if (videoBuffer && this.mediaBuffer !== videoBuffer) {
          _logger.logger.log('switching on alternate audio, use video.buffered to schedule main fragment loading');
          this.mediaBuffer = videoBuffer;
        }
      }
      this.altAudio = altAudio;
      this.tick();
    }
  }, {
    key: 'onBufferCreated',
    value: function onBufferCreated(data) {
      var tracks = data.tracks,
          mediaTrack = void 0,
          name = void 0,
          alternate = false;
      for (var type in tracks) {
        var track = tracks[type];
        if (track.id === 'main') {
          name = type;
          mediaTrack = track;
          // keep video source buffer reference
          if (type === 'video') {
            this.videoBuffer = tracks[type].buffer;
          }
        } else {
          alternate = true;
        }
      }
      if (alternate && mediaTrack) {
        _logger.logger.log('alternate track found, use ' + name + '.buffered to schedule main fragment loading');
        this.mediaBuffer = mediaTrack.buffer;
      } else {
        this.mediaBuffer = this.media;
      }
    }
  }, {
    key: 'onBufferAppended',
    value: function onBufferAppended(data) {
      if (data.parent === 'main') {
        var state = this.state;
        if (state === State.PARSING || state === State.PARSED) {
          // check if all buffers have been appended
          this.pendingBuffering = data.pending > 0;
          this._checkAppendedParsed();
        }
      }
    }
  }, {
    key: '_checkAppendedParsed',
    value: function _checkAppendedParsed() {
      //trigger handler right now
      if (this.state === State.PARSED && (!this.appended || !this.pendingBuffering)) {
        var frag = this.fragCurrent;
        if (frag) {
          var media = this.mediaBuffer ? this.mediaBuffer : this.media;
          _logger.logger.log('main buffered : ' + _timeRanges2.default.toString(media.buffered));
          // filter fragments potentially evicted from buffer. this is to avoid memleak on live streams
          var bufferedFrags = this._bufferedFrags.filter(function (frag) {
            return _bufferHelper2.default.isBuffered(media, (frag.startPTS + frag.endPTS) / 2);
          });
          // push new range
          bufferedFrags.push(frag);
          // sort frags, as we use BinarySearch for lookup in getBufferedFrag ...
          this._bufferedFrags = bufferedFrags.sort(function (a, b) {
            return a.startPTS - b.startPTS;
          });
          this.fragPrevious = frag;
          var stats = this.stats;
          stats.tbuffered = performance.now();
          // we should get rid of this.fragLastKbps
          this.fragLastKbps = Math.round(8 * stats.total / (stats.tbuffered - stats.tfirst));
          this.hls.trigger(_events2.default.FRAG_BUFFERED, { stats: stats, frag: frag, id: 'main' });
          this.state = State.IDLE;
        }
        this.tick();
      }
    }
  }, {
    key: 'onError',
    value: function onError(data) {
      var frag = data.frag || this.fragCurrent;
      // don't handle frag error not related to main fragment
      if (frag && frag.type !== 'main') {
        return;
      }
      var media = this.media,

      // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end
      mediaBuffered = media && _bufferHelper2.default.isBuffered(media, media.currentTime) && _bufferHelper2.default.isBuffered(media, media.currentTime + 0.5);
      switch (data.details) {
        case _errors.ErrorDetails.FRAG_LOAD_ERROR:
        case _errors.ErrorDetails.FRAG_LOAD_TIMEOUT:
        case _errors.ErrorDetails.KEY_LOAD_ERROR:
        case _errors.ErrorDetails.KEY_LOAD_TIMEOUT:
          if (!data.fatal) {
            var loadError = this.fragLoadError;
            if (loadError) {
              loadError++;
            } else {
              loadError = 1;
            }
            var config = this.config;
            // keep retrying / don't raise fatal network error if current position is buffered or if in automode with current level not 0
            if (loadError <= config.fragLoadingMaxRetry || mediaBuffered || frag.autoLevel && frag.level) {
              this.fragLoadError = loadError;
              // reset load counter to avoid frag loop loading error
              frag.loadCounter = 0;
              // exponential backoff capped to config.fragLoadingMaxRetryTimeout
              var delay = Math.min(Math.pow(2, loadError - 1) * config.fragLoadingRetryDelay, config.fragLoadingMaxRetryTimeout);
              _logger.logger.warn('mediaController: frag loading failed, retry in ' + delay + ' ms');
              this.retryDate = performance.now() + delay;
              // retry loading state
              // if loadedmetadata is not set, it means that we are emergency switch down on first frag
              // in that case, reset startFragRequested flag
              if (!this.loadedmetadata) {
                this.startFragRequested = false;
                this.nextLoadPosition = this.startPosition;
              }
              this.state = State.FRAG_LOADING_WAITING_RETRY;
            } else {
              _logger.logger.error('mediaController: ' + data.details + ' reaches max retry, redispatch as fatal ...');
              // switch error to fatal
              data.fatal = true;
              this.state = State.ERROR;
            }
          }
          break;
        case _errors.ErrorDetails.FRAG_LOOP_LOADING_ERROR:
          if (!data.fatal) {
            // if buffer is not empty
            if (mediaBuffered) {
              // try to reduce max buffer length : rationale is that we could get
              // frag loop loading error because of buffer eviction
              this._reduceMaxBufferLength(frag.duration);
              this.state = State.IDLE;
            } else {
              // buffer empty. report as fatal if in manual mode or if lowest level.
              // level controller takes care of emergency switch down logic
              if (!frag.autoLevel || frag.level === 0) {
                // switch error to fatal
                data.fatal = true;
                this.state = State.ERROR;
              }
            }
          }
          break;
        case _errors.ErrorDetails.LEVEL_LOAD_ERROR:
        case _errors.ErrorDetails.LEVEL_LOAD_TIMEOUT:
          if (this.state !== State.ERROR) {
            if (data.fatal) {
              // if fatal error, stop processing
              this.state = State.ERROR;
              _logger.logger.warn('streamController: ' + data.details + ',switch to ' + this.state + ' state ...');
            } else {
              // in case of non fatal error while loading level, if level controller is not retrying to load level , switch back to IDLE
              if (!data.levelRetry && this.state === State.WAITING_LEVEL) {
                this.state = State.IDLE;
              }
            }
          }
          break;
        case _errors.ErrorDetails.BUFFER_FULL_ERROR:
          // if in appending state
          if (data.parent === 'main' && (this.state === State.PARSING || this.state === State.PARSED)) {
            // reduce max buf len if current position is buffered
            if (mediaBuffered) {
              this._reduceMaxBufferLength(this.config.maxBufferLength);
              this.state = State.IDLE;
            } else {
              // current position is not buffered, but browser is still complaining about buffer full error
              // this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708
              // in that case flush the whole buffer to recover
              _logger.logger.warn('buffer full error also media.currentTime is not buffered, flush everything');
              this.fragCurrent = null;
              // flush everything
              this.flushMainBuffer(0, Number.POSITIVE_INFINITY);
            }
          }
          break;
        default:
          break;
      }
    }
  }, {
    key: '_reduceMaxBufferLength',
    value: function _reduceMaxBufferLength(minLength) {
      var config = this.config;
      if (config.maxMaxBufferLength >= minLength) {
        // reduce max buffer length as it might be too high. we do this to avoid loop flushing ...
        config.maxMaxBufferLength /= 2;
        _logger.logger.warn('main:reduce max buffer length to ' + config.maxMaxBufferLength + 's');
        // increase fragment load Index to avoid frag loop loading error after buffer flush
        this.fragLoadIdx += 2 * config.fragLoadingLoopThreshold;
      }
    }
  }, {
    key: '_checkBuffer',
    value: function _checkBuffer() {
      var media = this.media,
          config = this.config;
      // if ready state different from HAVE_NOTHING (numeric value 0), we are allowed to seek
      if (media && media.readyState) {
        var currentTime = media.currentTime,
            mediaBuffer = this.mediaBuffer ? this.mediaBuffer : media,
            buffered = mediaBuffer.buffered;
        // adjust currentTime to start position on loaded metadata
        if (!this.loadedmetadata && buffered.length) {
          this.loadedmetadata = true;
          // only adjust currentTime if different from startPosition or if startPosition not buffered
          // at that stage, there should be only one buffered range, as we reach that code after first fragment has been buffered
          var startPosition = media.seeking ? currentTime : this.startPosition,
              startPositionBuffered = _bufferHelper2.default.isBuffered(mediaBuffer, startPosition),
              firstbufferedPosition = buffered.start(0);
          // if currentTime not matching with expected startPosition or startPosition not buffered
          if (currentTime !== startPosition || !startPositionBuffered && Math.abs(startPosition - firstbufferedPosition) < config.maxSeekHole) {
            _logger.logger.log('target start position:' + startPosition);
            // if startPosition not buffered, let's seek to buffered.start(0)
            if (!startPositionBuffered) {
              startPosition = firstbufferedPosition;
              _logger.logger.log('target start position not buffered, seek to buffered.start(0) ' + startPosition);
            }
            _logger.logger.log('adjust currentTime from ' + currentTime + ' to ' + startPosition);
            media.currentTime = startPosition;
          }
        } else if (this.immediateSwitch) {
          this.immediateLevelSwitchEnd();
        } else {
          var bufferInfo = _bufferHelper2.default.bufferInfo(media, currentTime, 0),
              expectedPlaying = !(media.paused || // not playing when media is paused
          media.ended || // not playing when media is ended
          media.buffered.length === 0),
              // not playing if nothing buffered
          jumpThreshold = 0.5,
              // tolerance needed as some browsers stalls playback before reaching buffered range end
          playheadMoving = currentTime !== this.lastCurrentTime;

          if (playheadMoving) {
            // played moving, but was previously stalled => now not stuck anymore
            if (this.stallReported) {
              _logger.logger.warn('playback not stuck anymore @' + currentTime + ', after ' + Math.round(performance.now() - this.stalled) + 'ms');
              this.stallReported = false;
            }
            this.stalled = undefined;
            this.nudgeRetry = 0;
          } else {
            // playhead not moving
            if (expectedPlaying) {
              // playhead not moving BUT media expected to play
              var tnow = performance.now();
              var hls = this.hls;
              if (!this.stalled) {
                // stall just detected, store current time
                this.stalled = tnow;
                this.stallReported = false;
              } else {
                // playback already stalled, check stalling duration
                // if stalling for more than a given threshold, let's try to recover
                var stalledDuration = tnow - this.stalled;
                var bufferLen = bufferInfo.len;
                var nudgeRetry = this.nudgeRetry || 0;
                // have we reached stall deadline ?
                if (bufferLen <= jumpThreshold && stalledDuration > config.lowBufferWatchdogPeriod * 1000) {
                  // report stalled error once
                  if (!this.stallReported) {
                    this.stallReported = true;
                    _logger.logger.warn('playback stalling in low buffer @' + currentTime);
                    hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_STALLED_ERROR, fatal: false, buffer: bufferLen });
                  }
                  // if buffer len is below threshold, try to jump to start of next buffer range if close
                  // no buffer available @ currentTime, check if next buffer is close (within a config.maxSeekHole second range)
                  var nextBufferStart = bufferInfo.nextStart,
                      delta = nextBufferStart - currentTime;
                  if (nextBufferStart && delta < config.maxSeekHole && delta > 0) {
                    this.nudgeRetry = ++nudgeRetry;
                    var nudgeOffset = nudgeRetry * config.nudgeOffset;
                    // next buffer is close ! adjust currentTime to nextBufferStart
                    // this will ensure effective video decoding
                    _logger.logger.log('adjust currentTime from ' + media.currentTime + ' to next buffered @ ' + nextBufferStart + ' + nudge ' + nudgeOffset);
                    media.currentTime = nextBufferStart + nudgeOffset;
                    // reset stalled so to rearm watchdog timer
                    this.stalled = undefined;
                    hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_SEEK_OVER_HOLE, fatal: false, hole: nextBufferStart + nudgeOffset - currentTime });
                  }
                } else if (bufferLen > jumpThreshold && stalledDuration > config.highBufferWatchdogPeriod * 1000) {
                  // report stalled error once
                  if (!this.stallReported) {
                    this.stallReported = true;
                    _logger.logger.warn('playback stalling in high buffer @' + currentTime);
                    hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_STALLED_ERROR, fatal: false, buffer: bufferLen });
                  }
                  // reset stalled so to rearm watchdog timer
                  this.stalled = undefined;
                  this.nudgeRetry = ++nudgeRetry;
                  if (nudgeRetry < config.nudgeMaxRetry) {
                    var _currentTime = media.currentTime;
                    var targetTime = _currentTime + nudgeRetry * config.nudgeOffset;
                    _logger.logger.log('adjust currentTime from ' + _currentTime + ' to ' + targetTime);
                    // playback stalled in buffered area ... let's nudge currentTime to try to overcome this
                    media.currentTime = targetTime;
                    hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_NUDGE_ON_STALL, fatal: false });
                  } else {
                    _logger.logger.error('still stuck in high buffer @' + currentTime + ' after ' + config.nudgeMaxRetry + ', raise fatal error');
                    hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.BUFFER_STALLED_ERROR, fatal: true });
                  }
                }
              }
            }
          }
        }
      }
    }
  }, {
    key: 'onFragLoadEmergencyAborted',
    value: function onFragLoadEmergencyAborted() {
      this.state = State.IDLE;
      // if loadedmetadata is not set, it means that we are emergency switch down on first frag
      // in that case, reset startFragRequested flag
      if (!this.loadedmetadata) {
        this.startFragRequested = false;
        this.nextLoadPosition = this.startPosition;
      }
      this.tick();
    }
  }, {
    key: 'onBufferFlushed',
    value: function onBufferFlushed() {
      /* after successful buffer flushing, filter flushed fragments from bufferedFrags
        use mediaBuffered instead of media (so that we will check against video.buffered ranges in case of alt audio track)
      */
      var media = this.mediaBuffer ? this.mediaBuffer : this.media;
      this._bufferedFrags = this._bufferedFrags.filter(function (frag) {
        return _bufferHelper2.default.isBuffered(media, (frag.startPTS + frag.endPTS) / 2);
      });

      // increase fragment load Index to avoid frag loop loading error after buffer flush
      this.fragLoadIdx += 2 * this.config.fragLoadingLoopThreshold;
      // move to IDLE once flush complete. this should trigger new fragment loading
      this.state = State.IDLE;
      // reset reference to frag
      this.fragPrevious = null;
    }
  }, {
    key: 'swapAudioCodec',
    value: function swapAudioCodec() {
      this.audioCodecSwap = !this.audioCodecSwap;
    }
  }, {
    key: 'computeLivePosition',
    value: function computeLivePosition(sliding, levelDetails) {
      var targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * levelDetails.targetduration;
      return sliding + Math.max(0, levelDetails.totalduration - targetLatency);
    }
  }, {
    key: 'state',
    set: function set(nextState) {
      if (this.state !== nextState) {
        var previousState = this.state;
        this._state = nextState;
        _logger.logger.log('main stream:' + previousState + '->' + nextState);
        this.hls.trigger(_events2.default.STREAM_STATE_TRANSITION, { previousState: previousState, nextState: nextState });
      }
    },
    get: function get() {
      return this._state;
    }
  }, {
    key: 'currentLevel',
    get: function get() {
      var media = this.media;
      if (media) {
        var frag = this.getBufferedFrag(media.currentTime);
        if (frag) {
          return frag.level;
        }
      }
      return -1;
    }
  }, {
    key: 'nextBufferedFrag',
    get: function get() {
      var media = this.media;
      if (media) {
        // first get end range of current fragment
        return this.followingBufferedFrag(this.getBufferedFrag(media.currentTime));
      } else {
        return null;
      }
    }
  }, {
    key: 'nextLevel',
    get: function get() {
      var frag = this.nextBufferedFrag;
      if (frag) {
        return frag.level;
      } else {
        return -1;
      }
    }
  }, {
    key: 'liveSyncPosition',
    get: function get() {
      return this._liveSyncPosition;
    },
    set: function set(value) {
      this._liveSyncPosition = value;
    }
  }]);

  return StreamController;
}(_eventHandler2.default);

exports.default = StreamController;

},{"25":25,"33":33,"34":34,"35":35,"37":37,"38":38,"48":48,"54":54,"55":55}],14:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _logger = _dereq_(54);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * Subtitle Stream Controller
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               */

var SubtitleStreamController = function (_EventHandler) {
  _inherits(SubtitleStreamController, _EventHandler);

  function SubtitleStreamController(hls) {
    _classCallCheck(this, SubtitleStreamController);

    var _this = _possibleConstructorReturn(this, (SubtitleStreamController.__proto__ || Object.getPrototypeOf(SubtitleStreamController)).call(this, hls, _events2.default.ERROR, _events2.default.SUBTITLE_TRACKS_UPDATED, _events2.default.SUBTITLE_TRACK_SWITCH, _events2.default.SUBTITLE_TRACK_LOADED, _events2.default.SUBTITLE_FRAG_PROCESSED));

    _this.config = hls.config;
    _this.vttFragSNsProcessed = {};
    _this.vttFragQueues = undefined;
    _this.currentlyProcessing = null;
    _this.currentTrackId = -1;
    return _this;
  }

  _createClass(SubtitleStreamController, [{
    key: 'destroy',
    value: function destroy() {
      _eventHandler2.default.prototype.destroy.call(this);
    }

    // Remove all queued items and create a new, empty queue for each track.

  }, {
    key: 'clearVttFragQueues',
    value: function clearVttFragQueues() {
      var _this2 = this;

      this.vttFragQueues = {};
      this.tracks.forEach(function (track) {
        _this2.vttFragQueues[track.id] = [];
      });
    }

    // If no frag is being processed and queue isn't empty, initiate processing of next frag in line.

  }, {
    key: 'nextFrag',
    value: function nextFrag() {
      if (this.currentlyProcessing === null && this.currentTrackId > -1 && this.vttFragQueues[this.currentTrackId].length) {
        var frag = this.currentlyProcessing = this.vttFragQueues[this.currentTrackId].shift();
        this.hls.trigger(_events2.default.FRAG_LOADING, { frag: frag });
      }
    }

    // When fragment has finished processing, add sn to list of completed if successful.

  }, {
    key: 'onSubtitleFragProcessed',
    value: function onSubtitleFragProcessed(data) {
      if (data.success) {
        this.vttFragSNsProcessed[data.frag.trackId].push(data.frag.sn);
      }
      this.currentlyProcessing = null;
      this.nextFrag();
    }

    // If something goes wrong, procede to next frag, if we were processing one.

  }, {
    key: 'onError',
    value: function onError(data) {
      var frag = data.frag;
      // don't handle frag error not related to subtitle fragment
      if (frag && frag.type !== 'subtitle') {
        return;
      }
      if (this.currentlyProcessing) {
        this.currentlyProcessing = null;
        this.nextFrag();
      }
    }

    // Got all new subtitle tracks.

  }, {
    key: 'onSubtitleTracksUpdated',
    value: function onSubtitleTracksUpdated(data) {
      var _this3 = this;

      _logger.logger.log('subtitle tracks updated');
      this.tracks = data.subtitleTracks;
      this.clearVttFragQueues();
      this.vttFragSNsProcessed = {};
      this.tracks.forEach(function (track) {
        _this3.vttFragSNsProcessed[track.id] = [];
      });
    }
  }, {
    key: 'onSubtitleTrackSwitch',
    value: function onSubtitleTrackSwitch(data) {
      this.currentTrackId = data.id;
      this.clearVttFragQueues();
    }

    // Got a new set of subtitle fragments.

  }, {
    key: 'onSubtitleTrackLoaded',
    value: function onSubtitleTrackLoaded(data) {
      var processedFragSNs = this.vttFragSNsProcessed[data.id],
          fragQueue = this.vttFragQueues[data.id],
          currentFragSN = !!this.currentlyProcessing ? this.currentlyProcessing.sn : -1;

      var alreadyProcessed = function alreadyProcessed(frag) {
        return processedFragSNs.indexOf(frag.sn) > -1;
      };

      var alreadyInQueue = function alreadyInQueue(frag) {
        return fragQueue.some(function (fragInQueue) {
          return fragInQueue.sn === frag.sn;
        });
      };

      // Add all fragments that haven't been, aren't currently being and aren't waiting to be processed, to queue.
      data.details.fragments.forEach(function (frag) {
        if (!(alreadyProcessed(frag) || frag.sn === currentFragSN || alreadyInQueue(frag))) {
          // Frags don't know their subtitle track ID, so let's just add that...
          frag.trackId = data.id;
          fragQueue.push(frag);
        }
      });

      this.nextFrag();
    }
  }]);

  return SubtitleStreamController;
}(_eventHandler2.default);

exports.default = SubtitleStreamController;

},{"34":34,"35":35,"54":54}],15:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _logger = _dereq_(54);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * audio track controller
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               */

function filterSubtitleTracks(textTrackList) {
  var tracks = [];
  for (var i = 0; i < textTrackList.length; i++) {
    if (textTrackList[i].kind === 'subtitles') {
      tracks.push(textTrackList[i]);
    }
  }
  return tracks;
}

var SubtitleTrackController = function (_EventHandler) {
  _inherits(SubtitleTrackController, _EventHandler);

  function SubtitleTrackController(hls) {
    _classCallCheck(this, SubtitleTrackController);

    var _this = _possibleConstructorReturn(this, (SubtitleTrackController.__proto__ || Object.getPrototypeOf(SubtitleTrackController)).call(this, hls, _events2.default.MEDIA_ATTACHED, _events2.default.MEDIA_DETACHING, _events2.default.MANIFEST_LOADING, _events2.default.MANIFEST_LOADED, _events2.default.SUBTITLE_TRACK_LOADED));

    _this.tracks = [];
    _this.trackId = -1;
    _this.media = undefined;
    return _this;
  }

  _createClass(SubtitleTrackController, [{
    key: '_onTextTracksChanged',
    value: function _onTextTracksChanged() {
      // Media is undefined when switching streams via loadSource()
      if (!this.media) {
        return;
      }

      var trackId = -1;
      var tracks = filterSubtitleTracks(this.media.textTracks);
      for (var id = 0; id < tracks.length; id++) {
        if (tracks[id].mode === 'showing') {
          trackId = id;
        }
      }

      // Setting current subtitleTrack will invoke code.
      this.subtitleTrack = trackId;
    }
  }, {
    key: 'destroy',
    value: function destroy() {
      _eventHandler2.default.prototype.destroy.call(this);
    }

    // Listen for subtitle track change, then extract the current track ID.

  }, {
    key: 'onMediaAttached',
    value: function onMediaAttached(data) {
      this.media = data.media;
      if (!this.media) {
        return;
      }

      this.trackChangeListener = this._onTextTracksChanged.bind(this);
      this.media.textTracks.addEventListener('change', this.trackChangeListener);
    }
  }, {
    key: 'onMediaDetaching',
    value: function onMediaDetaching() {
      if (!this.media) {
        return;
      }

      this.media.textTracks.removeEventListener('change', this.trackChangeListener);

      this.media = undefined;
    }

    // Reset subtitle tracks on manifest loading

  }, {
    key: 'onManifestLoading',
    value: function onManifestLoading() {
      this.tracks = [];
      this.trackId = -1;
    }

    // Fired whenever a new manifest is loaded.

  }, {
    key: 'onManifestLoaded',
    value: function onManifestLoaded(data) {
      var _this2 = this;

      var tracks = data.subtitles || [];
      var defaultFound = false;
      this.tracks = tracks;
      this.trackId = -1;
      this.hls.trigger(_events2.default.SUBTITLE_TRACKS_UPDATED, { subtitleTracks: tracks });

      // loop through available subtitle tracks and autoselect default if needed
      // TODO: improve selection logic to handle forced, etc
      tracks.forEach(function (track) {
        if (track.default) {
          _this2.subtitleTrack = track.id;
          defaultFound = true;
        }
      });
    }

    // Trigger subtitle track playlist reload.

  }, {
    key: 'onTick',
    value: function onTick() {
      var trackId = this.trackId;
      var subtitleTrack = this.tracks[trackId];
      if (!subtitleTrack) {
        return;
      }

      var details = subtitleTrack.details;
      // check if we need to load playlist for this subtitle Track
      if (details === undefined || details.live === true) {
        // track not retrieved yet, or live playlist we need to (re)load it
        _logger.logger.log('(re)loading playlist for subtitle track ' + trackId);
        this.hls.trigger(_events2.default.SUBTITLE_TRACK_LOADING, { url: subtitleTrack.url, id: trackId });
      }
    }
  }, {
    key: 'onSubtitleTrackLoaded',
    value: function onSubtitleTrackLoaded(data) {
      var _this3 = this;

      if (data.id < this.tracks.length) {
        _logger.logger.log('subtitle track ' + data.id + ' loaded');
        this.tracks[data.id].details = data.details;
        // check if current playlist is a live playlist
        if (data.details.live && !this.timer) {
          // if live playlist we will have to reload it periodically
          // set reload period to playlist target duration
          this.timer = setInterval(function () {
            _this3.onTick();
          }, 1000 * data.details.targetduration, this);
        }
        if (!data.details.live && this.timer) {
          // playlist is not live and timer is armed : stopping it
          clearInterval(this.timer);
          this.timer = null;
        }
      }
    }

    /** get alternate subtitle tracks list from playlist **/

  }, {
    key: 'setSubtitleTrackInternal',
    value: function setSubtitleTrackInternal(newId) {
      // check if level idx is valid
      if (newId >= 0 && newId < this.tracks.length) {
        // stopping live reloading timer if any
        if (this.timer) {
          clearInterval(this.timer);
          this.timer = null;
        }
        this.trackId = newId;
        _logger.logger.log('switching to subtitle track ' + newId);
        var subtitleTrack = this.tracks[newId];
        this.hls.trigger(_events2.default.SUBTITLE_TRACK_SWITCH, { id: newId });
        // check if we need to load playlist for this subtitle Track
        var details = subtitleTrack.details;
        if (details === undefined || details.live === true) {
          // track not retrieved yet, or live playlist we need to (re)load it
          _logger.logger.log('(re)loading playlist for subtitle track ' + newId);
          this.hls.trigger(_events2.default.SUBTITLE_TRACK_LOADING, { url: subtitleTrack.url, id: newId });
        }
      }
    }
  }, {
    key: 'subtitleTracks',
    get: function get() {
      return this.tracks;
    }

    /** get index of the selected subtitle track (index in subtitle track lists) **/

  }, {
    key: 'subtitleTrack',
    get: function get() {
      return this.trackId;
    }

    /** select a subtitle track, based on its index in subtitle track lists**/
    ,
    set: function set(subtitleTrackId) {
      if (this.trackId !== subtitleTrackId) {
        // || this.tracks[subtitleTrackId].details === undefined) {
        this.setSubtitleTrackInternal(subtitleTrackId);
      }
    }
  }]);

  return SubtitleTrackController;
}(_eventHandler2.default);

exports.default = SubtitleTrackController;

},{"34":34,"35":35,"54":54}],16:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _cea608Parser = _dereq_(49);

var _cea608Parser2 = _interopRequireDefault(_cea608Parser);

var _webvttParser = _dereq_(58);

var _webvttParser2 = _interopRequireDefault(_webvttParser);

var _logger = _dereq_(54);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * Timeline Controller
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               */

function clearCurrentCues(track) {
  if (track && track.cues) {
    while (track.cues.length > 0) {
      track.removeCue(track.cues[0]);
    }
  }
}

function reuseVttTextTrack(inUseTrack, manifestTrack) {
  return inUseTrack && inUseTrack.label === manifestTrack.name && !(inUseTrack.textTrack1 || inUseTrack.textTrack2);
}

function intersection(x1, x2, y1, y2) {
  return Math.min(x2, y2) - Math.max(x1, y1);
}

var TimelineController = function (_EventHandler) {
  _inherits(TimelineController, _EventHandler);

  function TimelineController(hls) {
    _classCallCheck(this, TimelineController);

    var _this = _possibleConstructorReturn(this, (TimelineController.__proto__ || Object.getPrototypeOf(TimelineController)).call(this, hls, _events2.default.MEDIA_ATTACHING, _events2.default.MEDIA_DETACHING, _events2.default.FRAG_PARSING_USERDATA, _events2.default.MANIFEST_LOADING, _events2.default.MANIFEST_LOADED, _events2.default.FRAG_LOADED, _events2.default.LEVEL_SWITCHING, _events2.default.INIT_PTS_FOUND));

    _this.hls = hls;
    _this.config = hls.config;
    _this.enabled = true;
    _this.Cues = hls.config.cueHandler;
    _this.textTracks = [];
    _this.tracks = [];
    _this.unparsedVttFrags = [];
    _this.initPTS = undefined;
    _this.cueRanges = [];

    if (_this.config.enableCEA708Captions) {
      var self = _this;
      var sendAddTrackEvent = function sendAddTrackEvent(track, media) {
        var e = null;
        try {
          e = new window.Event('addtrack');
        } catch (err) {
          //for IE11
          e = document.createEvent('Event');
          e.initEvent('addtrack', false, false);
        }
        e.track = track;
        media.dispatchEvent(e);
      };

      var channel1 = {
        'newCue': function newCue(startTime, endTime, screen) {
          if (!self.textTrack1) {
            //Enable reuse of existing text track.
            var existingTrack1 = self.getExistingTrack('1');
            if (!existingTrack1) {
              var textTrack1 = self.createTextTrack('captions', self.config.captionsTextTrack1Label, self.config.captionsTextTrack1LanguageCode);
              if (textTrack1) {
                textTrack1.textTrack1 = true;
                self.textTrack1 = textTrack1;
              }
            } else {
              self.textTrack1 = existingTrack1;
              clearCurrentCues(self.textTrack1);

              sendAddTrackEvent(self.textTrack1, self.media);
            }
          }
          self.addCues('textTrack1', startTime, endTime, screen);
        }
      };

      var channel2 = {
        'newCue': function newCue(startTime, endTime, screen) {
          if (!self.textTrack2) {
            //Enable reuse of existing text track.
            var existingTrack2 = self.getExistingTrack('2');
            if (!existingTrack2) {
              var textTrack2 = self.createTextTrack('captions', self.config.captionsTextTrack2Label, self.config.captionsTextTrack1LanguageCode);
              if (textTrack2) {
                textTrack2.textTrack2 = true;
                self.textTrack2 = textTrack2;
              }
            } else {
              self.textTrack2 = existingTrack2;
              clearCurrentCues(self.textTrack2);

              sendAddTrackEvent(self.textTrack2, self.media);
            }
          }
          self.addCues('textTrack2', startTime, endTime, screen);
        }
      };

      _this.cea608Parser = new _cea608Parser2.default(0, channel1, channel2);
    }
    return _this;
  }

  _createClass(TimelineController, [{
    key: 'addCues',
    value: function addCues(channel, startTime, endTime, screen) {
      // skip cues which overlap more than 50% with previously parsed time ranges
      var ranges = this.cueRanges;
      var merged = false;
      for (var i = ranges.length; i--;) {
        var cueRange = ranges[i];
        var overlap = intersection(cueRange[0], cueRange[1], startTime, endTime);
        if (overlap >= 0) {
          cueRange[0] = Math.min(cueRange[0], startTime);
          cueRange[1] = Math.max(cueRange[1], endTime);
          merged = true;
          if (overlap / (endTime - startTime) > 0.5) {
            return;
          }
        }
      }
      if (!merged) {
        ranges.push([startTime, endTime]);
      }
      this.Cues.newCue(this[channel], startTime, endTime, screen);
    }

    // Triggered when an initial PTS is found; used for synchronisation of WebVTT.

  }, {
    key: 'onInitPtsFound',
    value: function onInitPtsFound(data) {
      var _this2 = this;

      if (typeof this.initPTS === 'undefined') {
        this.initPTS = data.initPTS;
      }

      // Due to asynchrony, initial PTS may arrive later than the first VTT fragments are loaded.
      // Parse any unparsed fragments upon receiving the initial PTS.
      if (this.unparsedVttFrags.length) {
        this.unparsedVttFrags.forEach(function (frag) {
          _this2.onFragLoaded(frag);
        });
        this.unparsedVttFrags = [];
      }
    }
  }, {
    key: 'getExistingTrack',
    value: function getExistingTrack(channelNumber) {
      var media = this.media;
      if (media) {
        for (var i = 0; i < media.textTracks.length; i++) {
          var textTrack = media.textTracks[i];
          var propName = 'textTrack' + channelNumber;
          if (textTrack[propName] === true) {
            return textTrack;
          }
        }
      }
      return null;
    }
  }, {
    key: 'createTextTrack',
    value: function createTextTrack(kind, label, lang) {
      var media = this.media;
      if (media) {
        return media.addTextTrack(kind, label, lang);
      }
    }
  }, {
    key: 'destroy',
    value: function destroy() {
      _eventHandler2.default.prototype.destroy.call(this);
    }
  }, {
    key: 'onMediaAttaching',
    value: function onMediaAttaching(data) {
      this.media = data.media;
    }
  }, {
    key: 'onMediaDetaching',
    value: function onMediaDetaching() {
      clearCurrentCues(this.textTrack1);
      clearCurrentCues(this.textTrack2);
    }
  }, {
    key: 'onManifestLoading',
    value: function onManifestLoading() {
      this.lastSn = -1; // Detect discontiguity in fragment parsing
      this.prevCC = -1;
      this.vttCCs = { ccOffset: 0, presentationOffset: 0 }; // Detect discontinuity in subtitle manifests

      // clear outdated subtitles
      var media = this.media;
      if (media) {
        var textTracks = media.textTracks;
        if (textTracks) {
          for (var i = 0; i < textTracks.length; i++) {
            clearCurrentCues(textTracks[i]);
          }
        }
      }
    }
  }, {
    key: 'onManifestLoaded',
    value: function onManifestLoaded(data) {
      var _this3 = this;

      this.textTracks = [];
      this.unparsedVttFrags = this.unparsedVttFrags || [];
      this.initPTS = undefined;
      this.cueRanges = [];

      if (this.config.enableWebVTT) {
        this.tracks = data.subtitles || [];
        var inUseTracks = this.media ? this.media.textTracks : [];

        this.tracks.forEach(function (track, index) {
          var textTrack = void 0;
          if (index < inUseTracks.length) {
            var inUseTrack = inUseTracks[index];
            // Reuse tracks with the same label, but do not reuse 608/708 tracks
            if (reuseVttTextTrack(inUseTrack, track)) {
              textTrack = inUseTrack;
            }
          }
          if (!textTrack) {
            textTrack = _this3.createTextTrack('subtitles', track.name, track.lang);
          }
          textTrack.mode = track.default ? 'showing' : 'hidden';
          _this3.textTracks.push(textTrack);
        });
      }
    }
  }, {
    key: 'onLevelSwitching',
    value: function onLevelSwitching() {
      this.enabled = this.hls.currentLevel.closedCaptions !== 'NONE';
    }
  }, {
    key: 'onFragLoaded',
    value: function onFragLoaded(data) {
      var frag = data.frag,
          payload = data.payload;
      if (frag.type === 'main') {
        var sn = frag.sn;
        // if this frag isn't contiguous, clear the parser so cues with bad start/end times aren't added to the textTrack
        if (sn !== this.lastSn + 1) {
          var cea608Parser = this.cea608Parser;
          if (cea608Parser) {
            cea608Parser.reset();
          }
        }
        this.lastSn = sn;
      }
      // If fragment is subtitle type, parse as WebVTT.
      else if (frag.type === 'subtitle') {
          if (payload.byteLength) {
            // We need an initial synchronisation PTS. Store fragments as long as none has arrived.
            if (typeof this.initPTS === 'undefined') {
              this.unparsedVttFrags.push(data);
              return;
            }
            var vttCCs = this.vttCCs;
            if (!vttCCs[frag.cc]) {
              vttCCs[frag.cc] = { start: frag.start, prevCC: this.prevCC, new: true };
              this.prevCC = frag.cc;
            }
            var textTracks = this.textTracks,
                hls = this.hls;

            // Parse the WebVTT file contents.
            _webvttParser2.default.parse(payload, this.initPTS, vttCCs, frag.cc, function (cues) {
              var currentTrack = textTracks[frag.trackId];
              // Add cues and trigger event with success true.
              cues.forEach(function (cue) {
                // Sometimes there are cue overlaps on segmented vtts so the same
                // cue can appear more than once in different vtt files.
                // This avoid showing duplicated cues with same timecode and text.
                if (!currentTrack.cues.getCueById(cue.id)) {
                  try {
                    currentTrack.addCue(cue);
                  } catch (err) {
                    var textTrackCue = new window.TextTrackCue(cue.startTime, cue.endTime, cue.text);
                    textTrackCue.id = cue.id;
                    currentTrack.addCue(textTrackCue);
                  }
                }
              });
              hls.trigger(_events2.default.SUBTITLE_FRAG_PROCESSED, { success: true, frag: frag });
            }, function (e) {
              // Something went wrong while parsing. Trigger event with success false.
              _logger.logger.log('Failed to parse VTT cue: ' + e);
              hls.trigger(_events2.default.SUBTITLE_FRAG_PROCESSED, { success: false, frag: frag });
            });
          } else {
            // In case there is no payload, finish unsuccessfully.
            this.hls.trigger(_events2.default.SUBTITLE_FRAG_PROCESSED, { success: false, frag: frag });
          }
        }
    }
  }, {
    key: 'onFragParsingUserdata',
    value: function onFragParsingUserdata(data) {
      // push all of the CEA-708 messages into the interpreter
      // immediately. It will create the proper timestamps based on our PTS value
      if (this.enabled && this.config.enableCEA708Captions) {
        for (var i = 0; i < data.samples.length; i++) {
          var ccdatas = this.extractCea608Data(data.samples[i].bytes);
          this.cea608Parser.addData(data.samples[i].pts, ccdatas);
        }
      }
    }
  }, {
    key: 'extractCea608Data',
    value: function extractCea608Data(byteArray) {
      var count = byteArray[0] & 31;
      var position = 2;
      var tmpByte, ccbyte1, ccbyte2, ccValid, ccType;
      var actualCCBytes = [];

      for (var j = 0; j < count; j++) {
        tmpByte = byteArray[position++];
        ccbyte1 = 0x7F & byteArray[position++];
        ccbyte2 = 0x7F & byteArray[position++];
        ccValid = (4 & tmpByte) !== 0;
        ccType = 3 & tmpByte;

        if (ccbyte1 === 0 && ccbyte2 === 0) {
          continue;
        }

        if (ccValid) {
          if (ccType === 0) // || ccType === 1
            {
              actualCCBytes.push(ccbyte1);
              actualCCBytes.push(ccbyte2);
            }
        }
      }
      return actualCCBytes;
    }
  }]);

  return TimelineController;
}(_eventHandler2.default);

exports.default = TimelineController;

},{"34":34,"35":35,"49":49,"54":54,"58":58}],17:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var AESCrypto = function () {
  function AESCrypto(subtle, iv) {
    _classCallCheck(this, AESCrypto);

    this.subtle = subtle;
    this.aesIV = iv;
  }

  _createClass(AESCrypto, [{
    key: 'decrypt',
    value: function decrypt(data, key) {
      return this.subtle.decrypt({ name: 'AES-CBC', iv: this.aesIV }, key, data);
    }
  }]);

  return AESCrypto;
}();

exports.default = AESCrypto;

},{}],18:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var AESDecryptor = function () {
  function AESDecryptor() {
    _classCallCheck(this, AESDecryptor);

    // Static after running initTable
    this.rcon = [0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];
    this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)];
    this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)];
    this.sBox = new Uint32Array(256);
    this.invSBox = new Uint32Array(256);

    // Changes during runtime
    this.key = new Uint32Array(0);

    this.initTable();
  }

  // Using view.getUint32() also swaps the byte order.


  _createClass(AESDecryptor, [{
    key: 'uint8ArrayToUint32Array_',
    value: function uint8ArrayToUint32Array_(arrayBuffer) {
      var view = new DataView(arrayBuffer);
      var newArray = new Uint32Array(4);
      for (var i = 0; i < 4; i++) {
        newArray[i] = view.getUint32(i * 4);
      }
      return newArray;
    }
  }, {
    key: 'initTable',
    value: function initTable() {
      var sBox = this.sBox;
      var invSBox = this.invSBox;
      var subMix = this.subMix;
      var subMix0 = subMix[0];
      var subMix1 = subMix[1];
      var subMix2 = subMix[2];
      var subMix3 = subMix[3];
      var invSubMix = this.invSubMix;
      var invSubMix0 = invSubMix[0];
      var invSubMix1 = invSubMix[1];
      var invSubMix2 = invSubMix[2];
      var invSubMix3 = invSubMix[3];

      var d = new Uint32Array(256);
      var x = 0;
      var xi = 0;
      var i = 0;
      for (i = 0; i < 256; i++) {
        if (i < 128) {
          d[i] = i << 1;
        } else {
          d[i] = i << 1 ^ 0x11b;
        }
      }

      for (i = 0; i < 256; i++) {
        var sx = xi ^ xi << 1 ^ xi << 2 ^ xi << 3 ^ xi << 4;
        sx = sx >>> 8 ^ sx & 0xff ^ 0x63;
        sBox[x] = sx;
        invSBox[sx] = x;

        // Compute multiplication
        var x2 = d[x];
        var x4 = d[x2];
        var x8 = d[x4];

        // Compute sub/invSub bytes, mix columns tables
        var t = d[sx] * 0x101 ^ sx * 0x1010100;
        subMix0[x] = t << 24 | t >>> 8;
        subMix1[x] = t << 16 | t >>> 16;
        subMix2[x] = t << 8 | t >>> 24;
        subMix3[x] = t;

        // Compute inv sub bytes, inv mix columns tables
        t = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100;
        invSubMix0[sx] = t << 24 | t >>> 8;
        invSubMix1[sx] = t << 16 | t >>> 16;
        invSubMix2[sx] = t << 8 | t >>> 24;
        invSubMix3[sx] = t;

        // Compute next counter
        if (!x) {
          x = xi = 1;
        } else {
          x = x2 ^ d[d[d[x8 ^ x2]]];
          xi ^= d[d[xi]];
        }
      }
    }
  }, {
    key: 'expandKey',
    value: function expandKey(keyBuffer) {
      // convert keyBuffer to Uint32Array
      var key = this.uint8ArrayToUint32Array_(keyBuffer);
      var sameKey = true;
      var offset = 0;

      while (offset < key.length && sameKey) {
        sameKey = key[offset] === this.key[offset];
        offset++;
      }

      if (sameKey) {
        return;
      }

      this.key = key;
      var keySize = this.keySize = key.length;

      if (keySize !== 4 && keySize !== 6 && keySize !== 8) {
        throw new Error('Invalid aes key size=' + keySize);
      }

      var ksRows = this.ksRows = (keySize + 6 + 1) * 4;
      var ksRow = void 0;
      var invKsRow = void 0;

      var keySchedule = this.keySchedule = new Uint32Array(ksRows);
      var invKeySchedule = this.invKeySchedule = new Uint32Array(ksRows);
      var sbox = this.sBox;
      var rcon = this.rcon;

      var invSubMix = this.invSubMix;
      var invSubMix0 = invSubMix[0];
      var invSubMix1 = invSubMix[1];
      var invSubMix2 = invSubMix[2];
      var invSubMix3 = invSubMix[3];

      var prev = void 0;
      var t = void 0;

      for (ksRow = 0; ksRow < ksRows; ksRow++) {
        if (ksRow < keySize) {
          prev = keySchedule[ksRow] = key[ksRow];
          continue;
        }
        t = prev;

        if (ksRow % keySize === 0) {
          // Rot word
          t = t << 8 | t >>> 24;

          // Sub word
          t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff];

          // Mix Rcon
          t ^= rcon[ksRow / keySize | 0] << 24;
        } else if (keySize > 6 && ksRow % keySize === 4) {
          // Sub word
          t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff];
        }

        keySchedule[ksRow] = prev = (keySchedule[ksRow - keySize] ^ t) >>> 0;
      }

      for (invKsRow = 0; invKsRow < ksRows; invKsRow++) {
        ksRow = ksRows - invKsRow;
        if (invKsRow & 3) {
          t = keySchedule[ksRow];
        } else {
          t = keySchedule[ksRow - 4];
        }

        if (invKsRow < 4 || ksRow <= 4) {
          invKeySchedule[invKsRow] = t;
        } else {
          invKeySchedule[invKsRow] = invSubMix0[sbox[t >>> 24]] ^ invSubMix1[sbox[t >>> 16 & 0xff]] ^ invSubMix2[sbox[t >>> 8 & 0xff]] ^ invSubMix3[sbox[t & 0xff]];
        }

        invKeySchedule[invKsRow] = invKeySchedule[invKsRow] >>> 0;
      }
    }

    // Adding this as a method greatly improves performance.

  }, {
    key: 'networkToHostOrderSwap',
    value: function networkToHostOrderSwap(word) {
      return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24;
    }
  }, {
    key: 'decrypt',
    value: function decrypt(inputArrayBuffer, offset, aesIV) {
      var nRounds = this.keySize + 6;
      var invKeySchedule = this.invKeySchedule;
      var invSBOX = this.invSBox;

      var invSubMix = this.invSubMix;
      var invSubMix0 = invSubMix[0];
      var invSubMix1 = invSubMix[1];
      var invSubMix2 = invSubMix[2];
      var invSubMix3 = invSubMix[3];

      var initVector = this.uint8ArrayToUint32Array_(aesIV);
      var initVector0 = initVector[0];
      var initVector1 = initVector[1];
      var initVector2 = initVector[2];
      var initVector3 = initVector[3];

      var inputInt32 = new Int32Array(inputArrayBuffer);
      var outputInt32 = new Int32Array(inputInt32.length);

      var t0 = void 0,
          t1 = void 0,
          t2 = void 0,
          t3 = void 0;
      var s0 = void 0,
          s1 = void 0,
          s2 = void 0,
          s3 = void 0;
      var inputWords0 = void 0,
          inputWords1 = void 0,
          inputWords2 = void 0,
          inputWords3 = void 0;

      var ksRow, i;
      var swapWord = this.networkToHostOrderSwap;

      while (offset < inputInt32.length) {
        inputWords0 = swapWord(inputInt32[offset]);
        inputWords1 = swapWord(inputInt32[offset + 1]);
        inputWords2 = swapWord(inputInt32[offset + 2]);
        inputWords3 = swapWord(inputInt32[offset + 3]);

        s0 = inputWords0 ^ invKeySchedule[0];
        s1 = inputWords3 ^ invKeySchedule[1];
        s2 = inputWords2 ^ invKeySchedule[2];
        s3 = inputWords1 ^ invKeySchedule[3];

        ksRow = 4;

        // Iterate through the rounds of decryption
        for (i = 1; i < nRounds; i++) {
          t0 = invSubMix0[s0 >>> 24] ^ invSubMix1[s1 >> 16 & 0xff] ^ invSubMix2[s2 >> 8 & 0xff] ^ invSubMix3[s3 & 0xff] ^ invKeySchedule[ksRow];
          t1 = invSubMix0[s1 >>> 24] ^ invSubMix1[s2 >> 16 & 0xff] ^ invSubMix2[s3 >> 8 & 0xff] ^ invSubMix3[s0 & 0xff] ^ invKeySchedule[ksRow + 1];
          t2 = invSubMix0[s2 >>> 24] ^ invSubMix1[s3 >> 16 & 0xff] ^ invSubMix2[s0 >> 8 & 0xff] ^ invSubMix3[s1 & 0xff] ^ invKeySchedule[ksRow + 2];
          t3 = invSubMix0[s3 >>> 24] ^ invSubMix1[s0 >> 16 & 0xff] ^ invSubMix2[s1 >> 8 & 0xff] ^ invSubMix3[s2 & 0xff] ^ invKeySchedule[ksRow + 3];
          // Update state
          s0 = t0;
          s1 = t1;
          s2 = t2;
          s3 = t3;

          ksRow = ksRow + 4;
        }

        // Shift rows, sub bytes, add round key
        t0 = invSBOX[s0 >>> 24] << 24 ^ invSBOX[s1 >> 16 & 0xff] << 16 ^ invSBOX[s2 >> 8 & 0xff] << 8 ^ invSBOX[s3 & 0xff] ^ invKeySchedule[ksRow];
        t1 = invSBOX[s1 >>> 24] << 24 ^ invSBOX[s2 >> 16 & 0xff] << 16 ^ invSBOX[s3 >> 8 & 0xff] << 8 ^ invSBOX[s0 & 0xff] ^ invKeySchedule[ksRow + 1];
        t2 = invSBOX[s2 >>> 24] << 24 ^ invSBOX[s3 >> 16 & 0xff] << 16 ^ invSBOX[s0 >> 8 & 0xff] << 8 ^ invSBOX[s1 & 0xff] ^ invKeySchedule[ksRow + 2];
        t3 = invSBOX[s3 >>> 24] << 24 ^ invSBOX[s0 >> 16 & 0xff] << 16 ^ invSBOX[s1 >> 8 & 0xff] << 8 ^ invSBOX[s2 & 0xff] ^ invKeySchedule[ksRow + 3];
        ksRow = ksRow + 3;

        // Write
        outputInt32[offset] = swapWord(t0 ^ initVector0);
        outputInt32[offset + 1] = swapWord(t3 ^ initVector1);
        outputInt32[offset + 2] = swapWord(t2 ^ initVector2);
        outputInt32[offset + 3] = swapWord(t1 ^ initVector3);

        // reset initVector to last 4 unsigned int
        initVector0 = inputWords0;
        initVector1 = inputWords1;
        initVector2 = inputWords2;
        initVector3 = inputWords3;

        offset = offset + 4;
      }

      return outputInt32.buffer;
    }
  }, {
    key: 'destroy',
    value: function destroy() {
      this.key = undefined;
      this.keySize = undefined;
      this.ksRows = undefined;

      this.sBox = undefined;
      this.invSBox = undefined;
      this.subMix = undefined;
      this.invSubMix = undefined;
      this.keySchedule = undefined;
      this.invKeySchedule = undefined;

      this.rcon = undefined;
    }
  }]);

  return AESDecryptor;
}();

exports.default = AESDecryptor;

},{}],19:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _aesCrypto = _dereq_(17);

var _aesCrypto2 = _interopRequireDefault(_aesCrypto);

var _fastAesKey = _dereq_(20);

var _fastAesKey2 = _interopRequireDefault(_fastAesKey);

var _aesDecryptor = _dereq_(18);

var _aesDecryptor2 = _interopRequireDefault(_aesDecryptor);

var _errors = _dereq_(33);

var _logger = _dereq_(54);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

/*globals self: false */

var Decrypter = function () {
  function Decrypter(observer, config) {
    _classCallCheck(this, Decrypter);

    this.observer = observer;
    this.config = config;
    this.logEnabled = true;
    try {
      var browserCrypto = crypto ? crypto : self.crypto;
      this.subtle = browserCrypto.subtle || browserCrypto.webkitSubtle;
    } catch (e) {}
    this.disableWebCrypto = !this.subtle;
  }

  _createClass(Decrypter, [{
    key: 'isSync',
    value: function isSync() {
      return this.disableWebCrypto && this.config.enableSoftwareAES;
    }
  }, {
    key: 'decrypt',
    value: function decrypt(data, key, iv, callback) {
      var _this = this;

      if (this.disableWebCrypto && this.config.enableSoftwareAES) {
        if (this.logEnabled) {
          _logger.logger.log('JS AES decrypt');
          this.logEnabled = false;
        }
        var decryptor = this.decryptor;
        if (!decryptor) {
          this.decryptor = decryptor = new _aesDecryptor2.default();
        }
        decryptor.expandKey(key);
        callback(decryptor.decrypt(data, 0, iv));
      } else {
        if (this.logEnabled) {
          _logger.logger.log('WebCrypto AES decrypt');
          this.logEnabled = false;
        }
        var subtle = this.subtle;
        if (this.key !== key) {
          this.key = key;
          this.fastAesKey = new _fastAesKey2.default(subtle, key);
        }

        this.fastAesKey.expandKey().then(function (aesKey) {
          // decrypt using web crypto
          var crypto = new _aesCrypto2.default(subtle, iv);
          crypto.decrypt(data, aesKey).catch(function (err) {
            _this.onWebCryptoError(err, data, key, iv, callback);
          }).then(function (result) {
            callback(result);
          });
        }).catch(function (err) {
          _this.onWebCryptoError(err, data, key, iv, callback);
        });
      }
    }
  }, {
    key: 'onWebCryptoError',
    value: function onWebCryptoError(err, data, key, iv, callback) {
      if (this.config.enableSoftwareAES) {
        _logger.logger.log('WebCrypto Error, disable WebCrypto API');
        this.disableWebCrypto = true;
        this.logEnabled = true;
        this.decrypt(data, key, iv, callback);
      } else {
        _logger.logger.error('decrypting error : ' + err.message);
        this.observer.trigger(Event.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.FRAG_DECRYPT_ERROR, fatal: true, reason: err.message });
      }
    }
  }, {
    key: 'destroy',
    value: function destroy() {
      var decryptor = this.decryptor;
      if (decryptor) {
        decryptor.destroy();
        this.decryptor = undefined;
      }
    }
  }]);

  return Decrypter;
}();

exports.default = Decrypter;

},{"17":17,"18":18,"20":20,"33":33,"54":54}],20:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var FastAESKey = function () {
  function FastAESKey(subtle, key) {
    _classCallCheck(this, FastAESKey);

    this.subtle = subtle;
    this.key = key;
  }

  _createClass(FastAESKey, [{
    key: 'expandKey',
    value: function expandKey() {
      return this.subtle.importKey('raw', this.key, { name: 'AES-CBC' }, false, ['encrypt', 'decrypt']);
    }
  }]);

  return FastAESKey;
}();

exports.default = FastAESKey;

},{}],21:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }(); /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * AAC demuxer
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      */


var _adts = _dereq_(22);

var _adts2 = _interopRequireDefault(_adts);

var _logger = _dereq_(54);

var _id = _dereq_(27);

var _id2 = _interopRequireDefault(_id);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var AACDemuxer = function () {
  function AACDemuxer(observer, remuxer, config) {
    _classCallCheck(this, AACDemuxer);

    this.observer = observer;
    this.config = config;
    this.remuxer = remuxer;
  }

  _createClass(AACDemuxer, [{
    key: 'resetInitSegment',
    value: function resetInitSegment(initSegment, audioCodec, videoCodec, duration) {
      this._audioTrack = { container: 'audio/adts', type: 'audio', id: -1, sequenceNumber: 0, isAAC: true, samples: [], len: 0, manifestCodec: audioCodec, duration: duration, inputTimeScale: 90000 };
    }
  }, {
    key: 'resetTimeStamp',
    value: function resetTimeStamp() {}
  }, {
    key: 'append',


    // feed incoming data to the front of the parsing pipeline
    value: function append(data, timeOffset, contiguous, accurateTimeOffset) {
      var track = this._audioTrack,
          id3Data = _id2.default.getID3Data(data, 0),
          pts = 90 * _id2.default.getTimeStamp(id3Data),
          frameIndex = 0,
          stamp = pts,
          length = data.length,
          offset = id3Data.length;

      var id3Samples = [{ pts: stamp, dts: stamp, data: id3Data }];

      while (offset < length - 1) {
        if (_adts2.default.isHeader(data, offset) && offset + 5 < length) {
          _adts2.default.initTrackConfig(track, this.observer, data, offset, track.manifestCodec);
          var frame = _adts2.default.appendFrame(track, data, offset, pts, frameIndex);
          if (frame) {
            offset += frame.length;
            stamp = frame.sample.pts;
            frameIndex++;
          } else {
            _logger.logger.log('Unable to parse AAC frame');
            break;
          }
        } else if (_id2.default.isHeader(data, offset)) {
          id3Data = _id2.default.getID3Data(data, offset);
          id3Samples.push({ pts: stamp, dts: stamp, data: id3Data });
          offset += id3Data.length;
        } else {
          //nothing found, keep looking
          offset++;
        }
      }

      this.remuxer.remux(track, { samples: [] }, { samples: id3Samples, inputTimeScale: 90000 }, { samples: [] }, timeOffset, contiguous, accurateTimeOffset);
    }
  }, {
    key: 'destroy',
    value: function destroy() {}
  }], [{
    key: 'probe',
    value: function probe(data) {
      // check if data contains ID3 timestamp and ADTS sync word
      var offset, length;
      var id3Data = _id2.default.getID3Data(data, 0);
      if (id3Data && _id2.default.getTimeStamp(id3Data) !== undefined) {
        // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1
        // Layer bits (position 14 and 15) in header should be always 0 for ADTS
        // More info https://wiki.multimedia.cx/index.php?title=ADTS
        for (offset = id3Data.length, length = Math.min(data.length - 1, offset + 100); offset < length; offset++) {
          if (_adts2.default.probe(data, offset)) {
            _logger.logger.log('ADTS sync word found !');
            return true;
          }
        }
      }
      return false;
    }
  }]);

  return AACDemuxer;
}();

exports.default = AACDemuxer;

},{"22":22,"27":27,"54":54}],22:[function(_dereq_,module,exports){
'use strict';

var _logger = _dereq_(54);

var _errors = _dereq_(33);

/**
 *  ADTS parser helper
 */
var ADTS = {
  getAudioConfig: function getAudioConfig(observer, data, offset, audioCodec) {
    var adtsObjectType,
        // :int
    adtsSampleingIndex,
        // :int
    adtsExtensionSampleingIndex,
        // :int
    adtsChanelConfig,
        // :int
    config,
        userAgent = navigator.userAgent.toLowerCase(),
        manifestCodec = audioCodec,
        adtsSampleingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
    // byte 2
    adtsObjectType = ((data[offset + 2] & 0xC0) >>> 6) + 1;
    adtsSampleingIndex = (data[offset + 2] & 0x3C) >>> 2;
    if (adtsSampleingIndex > adtsSampleingRates.length - 1) {
      observer.trigger(Event.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.FRAG_PARSING_ERROR, fatal: true, reason: 'invalid ADTS sampling index:' + adtsSampleingIndex });
      return;
    }
    adtsChanelConfig = (data[offset + 2] & 0x01) << 2;
    // byte 3
    adtsChanelConfig |= (data[offset + 3] & 0xC0) >>> 6;
    _logger.logger.log('manifest codec:' + audioCodec + ',ADTS data:type:' + adtsObjectType + ',sampleingIndex:' + adtsSampleingIndex + '[' + adtsSampleingRates[adtsSampleingIndex] + 'Hz],channelConfig:' + adtsChanelConfig);
    // firefox: freq less than 24kHz = AAC SBR (HE-AAC)
    if (/firefox/i.test(userAgent)) {
      if (adtsSampleingIndex >= 6) {
        adtsObjectType = 5;
        config = new Array(4);
        // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies
        // there is a factor 2 between frame sample rate and output sample rate
        // multiply frequency by 2 (see table below, equivalent to substract 3)
        adtsExtensionSampleingIndex = adtsSampleingIndex - 3;
      } else {
        adtsObjectType = 2;
        config = new Array(2);
        adtsExtensionSampleingIndex = adtsSampleingIndex;
      }
      // Android : always use AAC
    } else if (userAgent.indexOf('android') !== -1) {
      adtsObjectType = 2;
      config = new Array(2);
      adtsExtensionSampleingIndex = adtsSampleingIndex;
    } else {
      /*  for other browsers (Chrome/Vivaldi/Opera ...)
          always force audio type to be HE-AAC SBR, as some browsers do not support audio codec switch properly (like Chrome ...)
      */
      adtsObjectType = 5;
      config = new Array(4);
      // if (manifest codec is HE-AAC or HE-AACv2) OR (manifest codec not specified AND frequency less than 24kHz)
      if (audioCodec && (audioCodec.indexOf('mp4a.40.29') !== -1 || audioCodec.indexOf('mp4a.40.5') !== -1) || !audioCodec && adtsSampleingIndex >= 6) {
        // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies
        // there is a factor 2 between frame sample rate and output sample rate
        // multiply frequency by 2 (see table below, equivalent to substract 3)
        adtsExtensionSampleingIndex = adtsSampleingIndex - 3;
      } else {
        // if (manifest codec is AAC) AND (frequency less than 24kHz AND nb channel is 1) OR (manifest codec not specified and mono audio)
        // Chrome fails to play back with low frequency AAC LC mono when initialized with HE-AAC.  This is not a problem with stereo.
        if (audioCodec && audioCodec.indexOf('mp4a.40.2') !== -1 && (adtsSampleingIndex >= 6 && adtsChanelConfig === 1 || /vivaldi/i.test(userAgent)) || !audioCodec && adtsChanelConfig === 1) {
          adtsObjectType = 2;
          config = new Array(2);
        }
        adtsExtensionSampleingIndex = adtsSampleingIndex;
      }
    }
    /* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config
        ISO 14496-3 (AAC).pdf - Table 1.13 — Syntax of AudioSpecificConfig()
      Audio Profile / Audio Object Type
      0: Null
      1: AAC Main
      2: AAC LC (Low Complexity)
      3: AAC SSR (Scalable Sample Rate)
      4: AAC LTP (Long Term Prediction)
      5: SBR (Spectral Band Replication)
      6: AAC Scalable
     sampling freq
      0: 96000 Hz
      1: 88200 Hz
      2: 64000 Hz
      3: 48000 Hz
      4: 44100 Hz
      5: 32000 Hz
      6: 24000 Hz
      7: 22050 Hz
      8: 16000 Hz
      9: 12000 Hz
      10: 11025 Hz
      11: 8000 Hz
      12: 7350 Hz
      13: Reserved
      14: Reserved
      15: frequency is written explictly
      Channel Configurations
      These are the channel configurations:
      0: Defined in AOT Specifc Config
      1: 1 channel: front-center
      2: 2 channels: front-left, front-right
    */
    // audioObjectType = profile => profile, the MPEG-4 Audio Object Type minus 1
    config[0] = adtsObjectType << 3;
    // samplingFrequencyIndex
    config[0] |= (adtsSampleingIndex & 0x0E) >> 1;
    config[1] |= (adtsSampleingIndex & 0x01) << 7;
    // channelConfiguration
    config[1] |= adtsChanelConfig << 3;
    if (adtsObjectType === 5) {
      // adtsExtensionSampleingIndex
      config[1] |= (adtsExtensionSampleingIndex & 0x0E) >> 1;
      config[2] = (adtsExtensionSampleingIndex & 0x01) << 7;
      // adtsObjectType (force to 2, chrome is checking that object type is less than 5 ???
      //    https://chromium.googlesource.com/chromium/src.git/+/master/media/formats/mp4/aac.cc
      config[2] |= 2 << 2;
      config[3] = 0;
    }
    return { config: config, samplerate: adtsSampleingRates[adtsSampleingIndex], channelCount: adtsChanelConfig, codec: 'mp4a.40.' + adtsObjectType, manifestCodec: manifestCodec };
  },

  isHeaderPattern: function isHeaderPattern(data, offset) {
    return data[offset] === 0xff && (data[offset + 1] & 0xf6) === 0xf0;
  },

  getHeaderLength: function getHeaderLength(data, offset) {
    return !!(data[offset + 1] & 0x01) ? 7 : 9;
  },

  getFullFrameLength: function getFullFrameLength(data, offset) {
    return (data[offset + 3] & 0x03) << 11 | data[offset + 4] << 3 | (data[offset + 5] & 0xE0) >>> 5;
  },

  isHeader: function isHeader(data, offset) {
    // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1
    // Layer bits (position 14 and 15) in header should be always 0 for ADTS
    // More info https://wiki.multimedia.cx/index.php?title=ADTS
    if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) {
      return true;
    }
    return false;
  },

  probe: function probe(data, offset) {
    // same as isHeader but we also check that ADTS frame follows last ADTS frame
    // or end of data is reached
    if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) {
      // ADTS header Length
      var headerLength = this.getHeaderLength(data, offset);
      // ADTS frame Length
      var frameLength = headerLength;
      if (offset + 5 < data.length) {
        frameLength = this.getFullFrameLength(data, offset);
      }
      var newOffset = offset + frameLength;
      if (newOffset === data.length || newOffset + 1 < data.length && this.isHeaderPattern(data, newOffset)) {
        return true;
      }
    }
    return false;
  },

  initTrackConfig: function initTrackConfig(track, observer, data, offset, audioCodec) {
    if (!track.samplerate) {
      var config = this.getAudioConfig(observer, data, offset, audioCodec);
      track.config = config.config;
      track.samplerate = config.samplerate;
      track.channelCount = config.channelCount;
      track.codec = config.codec;
      track.manifestCodec = config.manifestCodec;
      _logger.logger.log('parsed codec:' + track.codec + ',rate:' + config.samplerate + ',nb channel:' + config.channelCount);
    }
  },

  getFrameDuration: function getFrameDuration(samplerate) {
    return 1024 * 90000 / samplerate;
  },

  appendFrame: function appendFrame(track, data, offset, pts, frameIndex) {
    var frameDuration = this.getFrameDuration(track.samplerate);
    var header = this.parseFrameHeader(data, offset, pts, frameIndex, frameDuration);
    if (header) {
      var stamp = header.stamp;
      var headerLength = header.headerLength;
      var frameLength = header.frameLength;

      //logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`);
      var aacSample = { unit: data.subarray(offset + headerLength, offset + headerLength + frameLength), pts: stamp, dts: stamp };

      track.samples.push(aacSample);
      track.len += frameLength;

      return { sample: aacSample, length: frameLength + headerLength };
    }

    return undefined;
  },

  parseFrameHeader: function parseFrameHeader(data, offset, pts, frameIndex, frameDuration) {
    var headerLength, frameLength, stamp;
    var length = data.length;

    // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header
    headerLength = this.getHeaderLength(data, offset);
    // retrieve frame size
    frameLength = this.getFullFrameLength(data, offset);
    frameLength -= headerLength;

    if (frameLength > 0 && offset + headerLength + frameLength <= length) {
      stamp = pts + frameIndex * frameDuration;
      //logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`);
      return { headerLength: headerLength, frameLength: frameLength, stamp: stamp };
    }

    return undefined;
  }
};

module.exports = ADTS;

},{"33":33,"54":54}],23:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }(); /*  inline demuxer.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      *   probe fragments and instantiate appropriate demuxer depending on content type (TSDemuxer, AACDemuxer, ...)
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      */

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _errors = _dereq_(33);

var _decrypter = _dereq_(19);

var _decrypter2 = _interopRequireDefault(_decrypter);

var _aacdemuxer = _dereq_(21);

var _aacdemuxer2 = _interopRequireDefault(_aacdemuxer);

var _mp4demuxer = _dereq_(29);

var _mp4demuxer2 = _interopRequireDefault(_mp4demuxer);

var _tsdemuxer = _dereq_(32);

var _tsdemuxer2 = _interopRequireDefault(_tsdemuxer);

var _mp3demuxer = _dereq_(28);

var _mp3demuxer2 = _interopRequireDefault(_mp3demuxer);

var _mp4Remuxer = _dereq_(45);

var _mp4Remuxer2 = _interopRequireDefault(_mp4Remuxer);

var _passthroughRemuxer = _dereq_(46);

var _passthroughRemuxer2 = _interopRequireDefault(_passthroughRemuxer);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var DemuxerInline = function () {
  function DemuxerInline(observer, typeSupported, config, vendor) {
    _classCallCheck(this, DemuxerInline);

    this.observer = observer;
    this.typeSupported = typeSupported;
    this.config = config;
    this.vendor = vendor;
  }

  _createClass(DemuxerInline, [{
    key: 'destroy',
    value: function destroy() {
      var demuxer = this.demuxer;
      if (demuxer) {
        demuxer.destroy();
      }
    }
  }, {
    key: 'push',
    value: function push(data, decryptdata, initSegment, audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS) {
      if (data.byteLength > 0 && decryptdata != null && decryptdata.key != null && decryptdata.method === 'AES-128') {
        var decrypter = this.decrypter;
        if (decrypter == null) {
          decrypter = this.decrypter = new _decrypter2.default(this.observer, this.config);
        }
        var localthis = this;
        // performance.now() not available on WebWorker, at least on Safari Desktop
        var startTime;
        try {
          startTime = performance.now();
        } catch (error) {
          startTime = Date.now();
        }
        decrypter.decrypt(data, decryptdata.key.buffer, decryptdata.iv.buffer, function (decryptedData) {
          var endTime;
          try {
            endTime = performance.now();
          } catch (error) {
            endTime = Date.now();
          }
          localthis.observer.trigger(_events2.default.FRAG_DECRYPTED, { stats: { tstart: startTime, tdecrypt: endTime } });
          localthis.pushDecrypted(new Uint8Array(decryptedData), decryptdata, new Uint8Array(initSegment), audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS);
        });
      } else {
        this.pushDecrypted(new Uint8Array(data), decryptdata, new Uint8Array(initSegment), audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS);
      }
    }
  }, {
    key: 'pushDecrypted',
    value: function pushDecrypted(data, decryptdata, initSegment, audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS) {
      var demuxer = this.demuxer;
      if (!demuxer ||
      // in case of continuity change, we might switch from content type (AAC container to TS container for example)
      // so let's check that current demuxer is still valid
      discontinuity && !this.probe(data)) {
        var observer = this.observer;
        var typeSupported = this.typeSupported;
        var config = this.config;
        // probing order is AAC/MP3/TS/MP4
        var muxConfig = [{ demux: _aacdemuxer2.default, remux: _mp4Remuxer2.default }, { demux: _mp3demuxer2.default, remux: _mp4Remuxer2.default }, { demux: _tsdemuxer2.default, remux: _mp4Remuxer2.default }, { demux: _mp4demuxer2.default, remux: _passthroughRemuxer2.default }];

        // probe for content type
        for (var i = 0, len = muxConfig.length; i < len; i++) {
          var mux = muxConfig[i];
          var probe = mux.demux.probe;
          if (probe(data)) {
            var _remuxer = this.remuxer = new mux.remux(observer, config, typeSupported, this.vendor);
            demuxer = new mux.demux(observer, _remuxer, config, typeSupported);
            this.probe = probe;
            break;
          }
        }
        if (!demuxer) {
          observer.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.FRAG_PARSING_ERROR, fatal: true, reason: 'no demux matching with content found' });
          return;
        }
        this.demuxer = demuxer;
      }
      var remuxer = this.remuxer;

      if (discontinuity || trackSwitch) {
        demuxer.resetInitSegment(initSegment, audioCodec, videoCodec, duration);
        remuxer.resetInitSegment();
      }
      if (discontinuity) {
        demuxer.resetTimeStamp(defaultInitPTS);
        remuxer.resetTimeStamp(defaultInitPTS);
      }
      if (typeof demuxer.setDecryptData === 'function') {
        demuxer.setDecryptData(decryptdata);
      }
      demuxer.append(data, timeOffset, contiguous, accurateTimeOffset);
    }
  }]);

  return DemuxerInline;
}();

exports.default = DemuxerInline;

},{"19":19,"21":21,"28":28,"29":29,"32":32,"33":33,"35":35,"45":45,"46":46}],24:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _demuxerInline = _dereq_(23);

var _demuxerInline2 = _interopRequireDefault(_demuxerInline);

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _logger = _dereq_(54);

var _events3 = _dereq_(1);

var _events4 = _interopRequireDefault(_events3);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* demuxer web worker.
 *  - listen to worker message, and trigger DemuxerInline upon reception of Fragments.
 *  - provides MP4 Boxes back to main thread using [transferable objects](https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast) in order to minimize message passing overhead.
 */

var DemuxerWorker = function DemuxerWorker(self) {
  // observer setup
  var observer = new _events4.default();
  observer.trigger = function trigger(event) {
    for (var _len = arguments.length, data = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
      data[_key - 1] = arguments[_key];
    }

    observer.emit.apply(observer, [event, event].concat(data));
  };

  observer.off = function off(event) {
    for (var _len2 = arguments.length, data = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
      data[_key2 - 1] = arguments[_key2];
    }

    observer.removeListener.apply(observer, [event].concat(data));
  };

  var forwardMessage = function forwardMessage(ev, data) {
    self.postMessage({ event: ev, data: data });
  };

  self.addEventListener('message', function (ev) {
    var data = ev.data;
    //console.log('demuxer cmd:' + data.cmd);
    switch (data.cmd) {
      case 'init':
        var config = JSON.parse(data.config);
        self.demuxer = new _demuxerInline2.default(observer, data.typeSupported, config, data.vendor);
        try {
          (0, _logger.enableLogs)(config.debug === true);
        } catch (err) {
          console.warn('demuxerWorker: unable to enable logs');
        }
        // signal end of worker init
        forwardMessage('init', null);
        break;
      case 'demux':
        self.demuxer.push(data.data, data.decryptdata, data.initSegment, data.audioCodec, data.videoCodec, data.timeOffset, data.discontinuity, data.trackSwitch, data.contiguous, data.duration, data.accurateTimeOffset, data.defaultInitPTS);
        break;
      default:
        break;
    }
  });

  // forward events to main thread
  observer.on(_events2.default.FRAG_DECRYPTED, forwardMessage);
  observer.on(_events2.default.FRAG_PARSING_INIT_SEGMENT, forwardMessage);
  observer.on(_events2.default.FRAG_PARSED, forwardMessage);
  observer.on(_events2.default.ERROR, forwardMessage);
  observer.on(_events2.default.FRAG_PARSING_METADATA, forwardMessage);
  observer.on(_events2.default.FRAG_PARSING_USERDATA, forwardMessage);
  observer.on(_events2.default.INIT_PTS_FOUND, forwardMessage);

  // special case for FRAG_PARSING_DATA: pass data1/data2 as transferable object (no copy)
  observer.on(_events2.default.FRAG_PARSING_DATA, function (ev, data) {
    var transferable = [];
    var message = { event: ev, data: data };
    if (data.data1) {
      message.data1 = data.data1.buffer;
      transferable.push(data.data1.buffer);
      delete data.data1;
    }
    if (data.data2) {
      message.data2 = data.data2.buffer;
      transferable.push(data.data2.buffer);
      delete data.data2;
    }
    self.postMessage(message, transferable);
  });
};

exports.default = DemuxerWorker;

},{"1":1,"23":23,"35":35,"54":54}],25:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _demuxerInline = _dereq_(23);

var _demuxerInline2 = _interopRequireDefault(_demuxerInline);

var _demuxerWorker = _dereq_(24);

var _demuxerWorker2 = _interopRequireDefault(_demuxerWorker);

var _logger = _dereq_(54);

var _errors = _dereq_(33);

var _events3 = _dereq_(1);

var _events4 = _interopRequireDefault(_events3);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Demuxer = function () {
  function Demuxer(hls, id) {
    _classCallCheck(this, Demuxer);

    this.hls = hls;
    this.id = id;
    // observer setup
    var observer = this.observer = new _events4.default();
    var config = hls.config;
    observer.trigger = function trigger(event) {
      for (var _len = arguments.length, data = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
        data[_key - 1] = arguments[_key];
      }

      observer.emit.apply(observer, [event, event].concat(data));
    };

    observer.off = function off(event) {
      for (var _len2 = arguments.length, data = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
        data[_key2 - 1] = arguments[_key2];
      }

      observer.removeListener.apply(observer, [event].concat(data));
    };

    var forwardMessage = function (ev, data) {
      data = data || {};
      data.frag = this.frag;
      data.id = this.id;
      hls.trigger(ev, data);
    }.bind(this);

    // forward events to main thread
    observer.on(_events2.default.FRAG_DECRYPTED, forwardMessage);
    observer.on(_events2.default.FRAG_PARSING_INIT_SEGMENT, forwardMessage);
    observer.on(_events2.default.FRAG_PARSING_DATA, forwardMessage);
    observer.on(_events2.default.FRAG_PARSED, forwardMessage);
    observer.on(_events2.default.ERROR, forwardMessage);
    observer.on(_events2.default.FRAG_PARSING_METADATA, forwardMessage);
    observer.on(_events2.default.FRAG_PARSING_USERDATA, forwardMessage);
    observer.on(_events2.default.INIT_PTS_FOUND, forwardMessage);

    var typeSupported = {
      mp4: MediaSource.isTypeSupported('video/mp4'),
      mpeg: MediaSource.isTypeSupported('audio/mpeg'),
      mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"')
    };
    // navigator.vendor is not always available in Web Worker
    // refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator
    var vendor = navigator.vendor;
    if (config.enableWorker && typeof Worker !== 'undefined') {
      _logger.logger.log('demuxing in webworker');
      var w = void 0;
      try {
        var work = _dereq_(3);
        w = this.w = work(_demuxerWorker2.default);
        this.onwmsg = this.onWorkerMessage.bind(this);
        w.addEventListener('message', this.onwmsg);
        w.onerror = function (event) {
          hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.OTHER_ERROR, details: _errors.ErrorDetails.INTERNAL_EXCEPTION, fatal: true, event: 'demuxerWorker', err: { message: event.message + ' (' + event.filename + ':' + event.lineno + ')' } });
        };
        w.postMessage({ cmd: 'init', typeSupported: typeSupported, vendor: vendor, id: id, config: JSON.stringify(config) });
      } catch (err) {
        _logger.logger.error('error while initializing DemuxerWorker, fallback on DemuxerInline');
        if (w) {
          // revoke the Object URL that was used to create demuxer worker, so as not to leak it
          URL.revokeObjectURL(w.objectURL);
        }
        this.demuxer = new _demuxerInline2.default(observer, typeSupported, config, vendor);
        this.w = undefined;
      }
    } else {
      this.demuxer = new _demuxerInline2.default(observer, typeSupported, config, vendor);
    }
  }

  _createClass(Demuxer, [{
    key: 'destroy',
    value: function destroy() {
      var w = this.w;
      if (w) {
        w.removeEventListener('message', this.onwmsg);
        w.terminate();
        this.w = null;
      } else {
        var demuxer = this.demuxer;
        if (demuxer) {
          demuxer.destroy();
          this.demuxer = null;
        }
      }
      var observer = this.observer;
      if (observer) {
        observer.removeAllListeners();
        this.observer = null;
      }
    }
  }, {
    key: 'push',
    value: function push(data, initSegment, audioCodec, videoCodec, frag, duration, accurateTimeOffset, defaultInitPTS) {
      var w = this.w;
      var timeOffset = !isNaN(frag.startDTS) ? frag.startDTS : frag.start;
      var decryptdata = frag.decryptdata;
      var lastFrag = this.frag;
      var discontinuity = !(lastFrag && frag.cc === lastFrag.cc);
      var trackSwitch = !(lastFrag && frag.level === lastFrag.level);
      var nextSN = lastFrag && frag.sn === lastFrag.sn + 1;
      var contiguous = !trackSwitch && nextSN;
      if (discontinuity) {
        _logger.logger.log(this.id + ':discontinuity detected');
      }
      if (trackSwitch) {
        _logger.logger.log(this.id + ':switch detected');
      }
      this.frag = frag;
      if (w) {
        // post fragment payload as transferable objects (no copy)
        w.postMessage({ cmd: 'demux', data: data, decryptdata: decryptdata, initSegment: initSegment, audioCodec: audioCodec, videoCodec: videoCodec, timeOffset: timeOffset, discontinuity: discontinuity, trackSwitch: trackSwitch, contiguous: contiguous, duration: duration, accurateTimeOffset: accurateTimeOffset, defaultInitPTS: defaultInitPTS }, [data]);
      } else {
        var demuxer = this.demuxer;
        if (demuxer) {
          demuxer.push(data, decryptdata, initSegment, audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS);
        }
      }
    }
  }, {
    key: 'onWorkerMessage',
    value: function onWorkerMessage(ev) {
      var data = ev.data,
          hls = this.hls;
      //console.log('onWorkerMessage:' + data.event);
      switch (data.event) {
        case 'init':
          // revoke the Object URL that was used to create demuxer worker, so as not to leak it
          URL.revokeObjectURL(this.w.objectURL);
          break;
        // special case for FRAG_PARSING_DATA: data1 and data2 are transferable objects
        case _events2.default.FRAG_PARSING_DATA:
          data.data.data1 = new Uint8Array(data.data1);
          if (data.data2) {
            data.data.data2 = new Uint8Array(data.data2);
          }
        /* falls through */
        default:
          data.data = data.data || {};
          data.data.frag = this.frag;
          data.data.id = this.id;
          hls.trigger(data.event, data.data);
          break;
      }
    }
  }]);

  return Demuxer;
}();

exports.default = Demuxer;

},{"1":1,"23":23,"24":24,"3":3,"33":33,"35":35,"54":54}],26:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }(); /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     */

var _logger = _dereq_(54);

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var ExpGolomb = function () {
  function ExpGolomb(data) {
    _classCallCheck(this, ExpGolomb);

    this.data = data;
    // the number of bytes left to examine in this.data
    this.bytesAvailable = data.byteLength;
    // the current word being examined
    this.word = 0; // :uint
    // the number of bits left to examine in the current word
    this.bitsAvailable = 0; // :uint
  }

  // ():void


  _createClass(ExpGolomb, [{
    key: 'loadWord',
    value: function loadWord() {
      var data = this.data,
          bytesAvailable = this.bytesAvailable,
          position = data.byteLength - bytesAvailable,
          workingBytes = new Uint8Array(4),
          availableBytes = Math.min(4, bytesAvailable);
      if (availableBytes === 0) {
        throw new Error('no bytes available');
      }
      workingBytes.set(data.subarray(position, position + availableBytes));
      this.word = new DataView(workingBytes.buffer).getUint32(0);
      // track the amount of this.data that has been processed
      this.bitsAvailable = availableBytes * 8;
      this.bytesAvailable -= availableBytes;
    }

    // (count:int):void

  }, {
    key: 'skipBits',
    value: function skipBits(count) {
      var skipBytes; // :int
      if (this.bitsAvailable > count) {
        this.word <<= count;
        this.bitsAvailable -= count;
      } else {
        count -= this.bitsAvailable;
        skipBytes = count >> 3;
        count -= skipBytes >> 3;
        this.bytesAvailable -= skipBytes;
        this.loadWord();
        this.word <<= count;
        this.bitsAvailable -= count;
      }
    }

    // (size:int):uint

  }, {
    key: 'readBits',
    value: function readBits(size) {
      var bits = Math.min(this.bitsAvailable, size),
          // :uint
      valu = this.word >>> 32 - bits; // :uint
      if (size > 32) {
        _logger.logger.error('Cannot read more than 32 bits at a time');
      }
      this.bitsAvailable -= bits;
      if (this.bitsAvailable > 0) {
        this.word <<= bits;
      } else if (this.bytesAvailable > 0) {
        this.loadWord();
      }
      bits = size - bits;
      if (bits > 0 && this.bitsAvailable) {
        return valu << bits | this.readBits(bits);
      } else {
        return valu;
      }
    }

    // ():uint

  }, {
    key: 'skipLZ',
    value: function skipLZ() {
      var leadingZeroCount; // :uint
      for (leadingZeroCount = 0; leadingZeroCount < this.bitsAvailable; ++leadingZeroCount) {
        if (0 !== (this.word & 0x80000000 >>> leadingZeroCount)) {
          // the first bit of working word is 1
          this.word <<= leadingZeroCount;
          this.bitsAvailable -= leadingZeroCount;
          return leadingZeroCount;
        }
      }
      // we exhausted word and still have not found a 1
      this.loadWord();
      return leadingZeroCount + this.skipLZ();
    }

    // ():void

  }, {
    key: 'skipUEG',
    value: function skipUEG() {
      this.skipBits(1 + this.skipLZ());
    }

    // ():void

  }, {
    key: 'skipEG',
    value: function skipEG() {
      this.skipBits(1 + this.skipLZ());
    }

    // ():uint

  }, {
    key: 'readUEG',
    value: function readUEG() {
      var clz = this.skipLZ(); // :uint
      return this.readBits(clz + 1) - 1;
    }

    // ():int

  }, {
    key: 'readEG',
    value: function readEG() {
      var valu = this.readUEG(); // :int
      if (0x01 & valu) {
        // the number is odd if the low order bit is set
        return 1 + valu >>> 1; // add 1 to make it even, and divide by 2
      } else {
        return -1 * (valu >>> 1); // divide by two then make it negative
      }
    }

    // Some convenience functions
    // :Boolean

  }, {
    key: 'readBoolean',
    value: function readBoolean() {
      return 1 === this.readBits(1);
    }

    // ():int

  }, {
    key: 'readUByte',
    value: function readUByte() {
      return this.readBits(8);
    }

    // ():int

  }, {
    key: 'readUShort',
    value: function readUShort() {
      return this.readBits(16);
    }
    // ():int

  }, {
    key: 'readUInt',
    value: function readUInt() {
      return this.readBits(32);
    }

    /**
     * Advance the ExpGolomb decoder past a scaling list. The scaling
     * list is optionally transmitted as part of a sequence parameter
     * set and is not relevant to transmuxing.
     * @param count {number} the number of entries in this scaling list
     * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
     */

  }, {
    key: 'skipScalingList',
    value: function skipScalingList(count) {
      var lastScale = 8,
          nextScale = 8,
          j,
          deltaScale;
      for (j = 0; j < count; j++) {
        if (nextScale !== 0) {
          deltaScale = this.readEG();
          nextScale = (lastScale + deltaScale + 256) % 256;
        }
        lastScale = nextScale === 0 ? lastScale : nextScale;
      }
    }

    /**
     * Read a sequence parameter set and return some interesting video
     * properties. A sequence parameter set is the H264 metadata that
     * describes the properties of upcoming video frames.
     * @param data {Uint8Array} the bytes of a sequence parameter set
     * @return {object} an object with configuration parsed from the
     * sequence parameter set, including the dimensions of the
     * associated video frames.
     */

  }, {
    key: 'readSPS',
    value: function readSPS() {
      var frameCropLeftOffset = 0,
          frameCropRightOffset = 0,
          frameCropTopOffset = 0,
          frameCropBottomOffset = 0,
          profileIdc,
          profileCompat,
          levelIdc,
          numRefFramesInPicOrderCntCycle,
          picWidthInMbsMinus1,
          picHeightInMapUnitsMinus1,
          frameMbsOnlyFlag,
          scalingListCount,
          i,
          readUByte = this.readUByte.bind(this),
          readBits = this.readBits.bind(this),
          readUEG = this.readUEG.bind(this),
          readBoolean = this.readBoolean.bind(this),
          skipBits = this.skipBits.bind(this),
          skipEG = this.skipEG.bind(this),
          skipUEG = this.skipUEG.bind(this),
          skipScalingList = this.skipScalingList.bind(this);

      readUByte();
      profileIdc = readUByte(); // profile_idc
      profileCompat = readBits(5); // constraint_set[0-4]_flag, u(5)
      skipBits(3); // reserved_zero_3bits u(3),
      levelIdc = readUByte(); //level_idc u(8)
      skipUEG(); // seq_parameter_set_id
      // some profiles have more optional data we don't need
      if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) {
        var chromaFormatIdc = readUEG();
        if (chromaFormatIdc === 3) {
          skipBits(1); // separate_colour_plane_flag
        }
        skipUEG(); // bit_depth_luma_minus8
        skipUEG(); // bit_depth_chroma_minus8
        skipBits(1); // qpprime_y_zero_transform_bypass_flag
        if (readBoolean()) {
          // seq_scaling_matrix_present_flag
          scalingListCount = chromaFormatIdc !== 3 ? 8 : 12;
          for (i = 0; i < scalingListCount; i++) {
            if (readBoolean()) {
              // seq_scaling_list_present_flag[ i ]
              if (i < 6) {
                skipScalingList(16);
              } else {
                skipScalingList(64);
              }
            }
          }
        }
      }
      skipUEG(); // log2_max_frame_num_minus4
      var picOrderCntType = readUEG();
      if (picOrderCntType === 0) {
        readUEG(); //log2_max_pic_order_cnt_lsb_minus4
      } else if (picOrderCntType === 1) {
        skipBits(1); // delta_pic_order_always_zero_flag
        skipEG(); // offset_for_non_ref_pic
        skipEG(); // offset_for_top_to_bottom_field
        numRefFramesInPicOrderCntCycle = readUEG();
        for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
          skipEG(); // offset_for_ref_frame[ i ]
        }
      }
      skipUEG(); // max_num_ref_frames
      skipBits(1); // gaps_in_frame_num_value_allowed_flag
      picWidthInMbsMinus1 = readUEG();
      picHeightInMapUnitsMinus1 = readUEG();
      frameMbsOnlyFlag = readBits(1);
      if (frameMbsOnlyFlag === 0) {
        skipBits(1); // mb_adaptive_frame_field_flag
      }
      skipBits(1); // direct_8x8_inference_flag
      if (readBoolean()) {
        // frame_cropping_flag
        frameCropLeftOffset = readUEG();
        frameCropRightOffset = readUEG();
        frameCropTopOffset = readUEG();
        frameCropBottomOffset = readUEG();
      }
      var pixelRatio = [1, 1];
      if (readBoolean()) {
        // vui_parameters_present_flag
        if (readBoolean()) {
          // aspect_ratio_info_present_flag
          var aspectRatioIdc = readUByte();
          switch (aspectRatioIdc) {
            case 1:
              pixelRatio = [1, 1];break;
            case 2:
              pixelRatio = [12, 11];break;
            case 3:
              pixelRatio = [10, 11];break;
            case 4:
              pixelRatio = [16, 11];break;
            case 5:
              pixelRatio = [40, 33];break;
            case 6:
              pixelRatio = [24, 11];break;
            case 7:
              pixelRatio = [20, 11];break;
            case 8:
              pixelRatio = [32, 11];break;
            case 9:
              pixelRatio = [80, 33];break;
            case 10:
              pixelRatio = [18, 11];break;
            case 11:
              pixelRatio = [15, 11];break;
            case 12:
              pixelRatio = [64, 33];break;
            case 13:
              pixelRatio = [160, 99];break;
            case 14:
              pixelRatio = [4, 3];break;
            case 15:
              pixelRatio = [3, 2];break;
            case 16:
              pixelRatio = [2, 1];break;
            case 255:
              {
                pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()];
                break;
              }
          }
        }
      }
      return {
        width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2),
        height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset),
        pixelRatio: pixelRatio
      };
    }
  }, {
    key: 'readSliceType',
    value: function readSliceType() {
      // skip NALu type
      this.readUByte();
      // discard first_mb_in_slice
      this.readUEG();
      // return slice_type
      return this.readUEG();
    }
  }]);

  return ExpGolomb;
}();

exports.default = ExpGolomb;

},{"54":54}],27:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

/**
 * ID3 parser
 */
var ID3 = function () {
  function ID3() {
    _classCallCheck(this, ID3);
  }

  _createClass(ID3, null, [{
    key: 'isHeader',

    /**
     * Returns true if an ID3 header can be found at offset in data
     * @param {Uint8Array} data - The data to search in
     * @param {number} offset - The offset at which to start searching
     * @return {boolean} - True if an ID3 header is found
     */
    value: function isHeader(data, offset) {
      /*
      * http://id3.org/id3v2.3.0
      * [0]     = 'I'
      * [1]     = 'D'
      * [2]     = '3'
      * [3,4]   = {Version}
      * [5]     = {Flags}
      * [6-9]   = {ID3 Size}
      *
      * An ID3v2 tag can be detected with the following pattern:
      *  $49 44 33 yy yy xx zz zz zz zz
      * Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80
      */
      if (offset + 10 <= data.length) {
        //look for 'ID3' identifier
        if (data[offset] === 0x49 && data[offset + 1] === 0x44 && data[offset + 2] === 0x33) {
          //check version is within range
          if (data[offset + 3] < 0xFF && data[offset + 4] < 0xFF) {
            //check size is within range
            if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) {
              return true;
            }
          }
        }
      }

      return false;
    }

    /**
     * Returns true if an ID3 footer can be found at offset in data
     * @param {Uint8Array} data - The data to search in
     * @param {number} offset - The offset at which to start searching
     * @return {boolean} - True if an ID3 footer is found
     */

  }, {
    key: 'isFooter',
    value: function isFooter(data, offset) {
      /*
      * The footer is a copy of the header, but with a different identifier
      */
      if (offset + 10 <= data.length) {
        //look for '3DI' identifier
        if (data[offset] === 0x33 && data[offset + 1] === 0x44 && data[offset + 2] === 0x49) {
          //check version is within range
          if (data[offset + 3] < 0xFF && data[offset + 4] < 0xFF) {
            //check size is within range
            if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) {
              return true;
            }
          }
        }
      }

      return false;
    }

    /**
     * Returns any adjacent ID3 tags found in data starting at offset, as one block of data
     * @param {Uint8Array} data - The data to search in
     * @param {number} offset - The offset at which to start searching
     * @return {Uint8Array} - The block of data containing any ID3 tags found
     */

  }, {
    key: 'getID3Data',
    value: function getID3Data(data, offset) {
      var front = offset;
      var length = 0;

      while (ID3.isHeader(data, offset)) {
        //ID3 header is 10 bytes
        length += 10;

        var size = ID3._readSize(data, offset + 6);
        length += size;

        if (ID3.isFooter(data, offset + 10)) {
          //ID3 footer is 10 bytes
          length += 10;
        }

        offset += length;
      }

      if (length > 0) {
        return data.subarray(front, front + length);
      }

      return undefined;
    }
  }, {
    key: '_readSize',
    value: function _readSize(data, offset) {
      var size = 0;
      size = (data[offset] & 0x7f) << 21;
      size |= (data[offset + 1] & 0x7f) << 14;
      size |= (data[offset + 2] & 0x7f) << 7;
      size |= data[offset + 3] & 0x7f;
      return size;
    }

    /**
     * Searches for the Elementary Stream timestamp found in the ID3 data chunk
     * @param {Uint8Array} data - Block of data containing one or more ID3 tags
     * @return {number} - The timestamp
     */

  }, {
    key: 'getTimeStamp',
    value: function getTimeStamp(data) {
      var frames = ID3.getID3Frames(data);
      for (var i = 0; i < frames.length; i++) {
        var frame = frames[i];
        if (ID3.isTimeStampFrame(frame)) {
          return ID3._readTimeStamp(frame);
        }
      }

      return undefined;
    }

    /**
     * Returns true if the ID3 frame is an Elementary Stream timestamp frame
     * @param {ID3 frame} frame
     */

  }, {
    key: 'isTimeStampFrame',
    value: function isTimeStampFrame(frame) {
      return frame && frame.key === 'PRIV' && frame.info === 'com.apple.streaming.transportStreamTimestamp';
    }
  }, {
    key: '_getFrameData',
    value: function _getFrameData(data) {
      /*
      Frame ID       $xx xx xx xx (four characters)
      Size           $xx xx xx xx
      Flags          $xx xx
      */
      var type = String.fromCharCode(data[0], data[1], data[2], data[3]);
      var size = ID3._readSize(data, 4);

      //skip frame id, size, and flags
      var offset = 10;

      return { type: type, size: size, data: data.subarray(offset, offset + size) };
    }

    /**
     * Returns an array of ID3 frames found in all the ID3 tags in the id3Data
     * @param {Uint8Array} id3Data - The ID3 data containing one or more ID3 tags
     * @return {ID3 frame[]} - Array of ID3 frame objects
     */

  }, {
    key: 'getID3Frames',
    value: function getID3Frames(id3Data) {
      var offset = 0;
      var frames = [];

      while (ID3.isHeader(id3Data, offset)) {
        var size = ID3._readSize(id3Data, offset + 6);
        //skip past ID3 header
        offset += 10;
        var end = offset + size;
        //loop through frames in the ID3 tag
        while (offset + 8 < end) {
          var frameData = ID3._getFrameData(id3Data.subarray(offset));
          var frame = ID3._decodeFrame(frameData);
          if (frame) {
            frames.push(frame);
          }
          //skip frame header and frame data
          offset += frameData.size + 10;
        }

        if (ID3.isFooter(id3Data, offset)) {
          offset += 10;
        }
      }

      return frames;
    }
  }, {
    key: '_decodeFrame',
    value: function _decodeFrame(frame) {
      if (frame.type === 'PRIV') {
        return ID3._decodePrivFrame(frame);
      } else if (frame.type[0] === 'T') {
        return ID3._decodeTextFrame(frame);
      } else if (frame.type[0] === 'W') {
        return ID3._decodeURLFrame(frame);
      }

      return undefined;
    }
  }, {
    key: '_readTimeStamp',
    value: function _readTimeStamp(timeStampFrame) {
      if (timeStampFrame.data.byteLength === 8) {
        var data = new Uint8Array(timeStampFrame.data);
        // timestamp is 33 bit expressed as a big-endian eight-octet number,
        // with the upper 31 bits set to zero.
        var pts33Bit = data[3] & 0x1;
        var timestamp = (data[4] << 23) + (data[5] << 15) + (data[6] << 7) + data[7];
        timestamp /= 45;

        if (pts33Bit) {
          timestamp += 47721858.84; // 2^32 / 90
        }

        return Math.round(timestamp);
      }

      return undefined;
    }
  }, {
    key: '_decodePrivFrame',
    value: function _decodePrivFrame(frame) {
      /*
      Format: <text string>\0<binary data>
      */
      if (frame.size < 2) {
        return undefined;
      }

      var owner = ID3._utf8ArrayToStr(frame.data);
      var privateData = new Uint8Array(frame.data.subarray(owner.length + 1));

      return { key: frame.type, info: owner, data: privateData.buffer };
    }
  }, {
    key: '_decodeTextFrame',
    value: function _decodeTextFrame(frame) {
      if (frame.size < 2) {
        return undefined;
      }

      if (frame.type === 'TXXX') {
        /*
        Format:
        [0]   = {Text Encoding}
        [1-?] = {Description}\0{Value}
        */
        var index = 1;
        var description = ID3._utf8ArrayToStr(frame.data.subarray(index));

        index += description.length + 1;
        var value = ID3._utf8ArrayToStr(frame.data.subarray(index));

        return { key: frame.type, info: description, data: value };
      } else {
        /*
        Format:
        [0]   = {Text Encoding}
        [1-?] = {Value}
        */
        var text = ID3._utf8ArrayToStr(frame.data.subarray(1));
        return { key: frame.type, data: text };
      }
    }
  }, {
    key: '_decodeURLFrame',
    value: function _decodeURLFrame(frame) {
      if (frame.type === 'WXXX') {
        /*
        Format:
        [0]   = {Text Encoding}
        [1-?] = {Description}\0{URL}
        */
        if (frame.size < 2) {
          return undefined;
        }

        var index = 1;
        var description = ID3._utf8ArrayToStr(frame.data.subarray(index));

        index += description.length + 1;
        var value = ID3._utf8ArrayToStr(frame.data.subarray(index));

        return { key: frame.type, info: description, data: value };
      } else {
        /*
        Format:
        [0-?] = {URL}
        */
        var url = ID3._utf8ArrayToStr(frame.data);
        return { key: frame.type, data: url };
      }
    }

    // http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197
    // http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
    /* utf.js - UTF-8 <=> UTF-16 convertion
     *
     * Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
     * Version: 1.0
     * LastModified: Dec 25 1999
     * This library is free.  You can redistribute it and/or modify it.
     */

  }, {
    key: '_utf8ArrayToStr',
    value: function _utf8ArrayToStr(array) {

      var char2 = void 0;
      var char3 = void 0;
      var out = '';
      var i = 0;
      var length = array.length;

      while (i < length) {
        var c = array[i++];
        switch (c >> 4) {
          case 0:
            return out;
          case 1:case 2:case 3:case 4:case 5:case 6:case 7:
            // 0xxxxxxx
            out += String.fromCharCode(c);
            break;
          case 12:case 13:
            // 110x xxxx   10xx xxxx
            char2 = array[i++];
            out += String.fromCharCode((c & 0x1F) << 6 | char2 & 0x3F);
            break;
          case 14:
            // 1110 xxxx  10xx xxxx  10xx xxxx
            char2 = array[i++];
            char3 = array[i++];
            out += String.fromCharCode((c & 0x0F) << 12 | (char2 & 0x3F) << 6 | (char3 & 0x3F) << 0);
            break;
        }
      }

      return out;
    }
  }]);

  return ID3;
}();

exports.default = ID3;

},{}],28:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }(); /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * MP3 demuxer
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      */


var _id = _dereq_(27);

var _id2 = _interopRequireDefault(_id);

var _logger = _dereq_(54);

var _mpegaudio = _dereq_(30);

var _mpegaudio2 = _interopRequireDefault(_mpegaudio);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var MP3Demuxer = function () {
  function MP3Demuxer(observer, remuxer, config) {
    _classCallCheck(this, MP3Demuxer);

    this.observer = observer;
    this.config = config;
    this.remuxer = remuxer;
  }

  _createClass(MP3Demuxer, [{
    key: 'resetInitSegment',
    value: function resetInitSegment(initSegment, audioCodec, videoCodec, duration) {
      this._audioTrack = { container: 'audio/mpeg', type: 'audio', id: -1, sequenceNumber: 0, isAAC: false, samples: [], len: 0, manifestCodec: audioCodec, duration: duration, inputTimeScale: 90000 };
    }
  }, {
    key: 'resetTimeStamp',
    value: function resetTimeStamp() {}
  }, {
    key: 'append',


    // feed incoming data to the front of the parsing pipeline
    value: function append(data, timeOffset, contiguous, accurateTimeOffset) {
      var id3Data = _id2.default.getID3Data(data, 0);
      var pts = 90 * _id2.default.getTimeStamp(id3Data);
      var offset = id3Data.length;
      var length = data.length;
      var frameIndex = 0,
          stamp = 0;
      var track = this._audioTrack;

      var id3Samples = [{ pts: pts, dts: pts, data: id3Data }];

      while (offset < length) {
        if (_mpegaudio2.default.isHeader(data, offset)) {
          var frame = _mpegaudio2.default.appendFrame(track, data, offset, pts, frameIndex);
          if (frame) {
            offset += frame.length;
            stamp = frame.sample.pts;
            frameIndex++;
          } else {
            //logger.log('Unable to parse Mpeg audio frame');
            break;
          }
        } else if (_id2.default.isHeader(data, offset)) {
          id3Data = _id2.default.getID3Data(data, offset);
          id3Samples.push({ pts: stamp, dts: stamp, data: id3Data });
          offset += id3Data.length;
        } else {
          //nothing found, keep looking
          offset++;
        }
      }

      this.remuxer.remux(track, { samples: [] }, { samples: id3Samples, inputTimeScale: 90000 }, { samples: [] }, timeOffset, contiguous, accurateTimeOffset);
    }
  }, {
    key: 'destroy',
    value: function destroy() {}
  }], [{
    key: 'probe',
    value: function probe(data) {
      // check if data contains ID3 timestamp and MPEG sync word
      var offset, length;
      var id3Data = _id2.default.getID3Data(data, 0);
      if (id3Data && _id2.default.getTimeStamp(id3Data) !== undefined) {
        // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1
        // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III)
        // More info http://www.mp3-tech.org/programmer/frame_header.html
        for (offset = id3Data.length, length = Math.min(data.length - 1, offset + 100); offset < length; offset++) {
          if (_mpegaudio2.default.probe(data, offset)) {
            _logger.logger.log('MPEG Audio sync word found !');
            return true;
          }
        }
      }
      return false;
    }
  }]);

  return MP3Demuxer;
}();

exports.default = MP3Demuxer;

},{"27":27,"30":30,"54":54}],29:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }(); /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * MP4 demuxer
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      */
//import {logger} from '../utils/logger';


var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var UINT32_MAX = Math.pow(2, 32) - 1;

var MP4Demuxer = function () {
  function MP4Demuxer(observer, remuxer) {
    _classCallCheck(this, MP4Demuxer);

    this.observer = observer;
    this.remuxer = remuxer;
  }

  _createClass(MP4Demuxer, [{
    key: 'resetTimeStamp',
    value: function resetTimeStamp(initPTS) {
      this.initPTS = initPTS;
    }
  }, {
    key: 'resetInitSegment',
    value: function resetInitSegment(initSegment, audioCodec, videoCodec, duration) {
      //jshint unused:false
      if (initSegment && initSegment.byteLength) {
        var initData = this.initData = MP4Demuxer.parseInitSegment(initSegment);
        var tracks = {};
        if (initData.audio) {
          tracks.audio = { container: 'audio/mp4', codec: audioCodec, initSegment: duration ? initSegment : null };
        }
        if (initData.video) {
          tracks.video = { container: 'video/mp4', codec: videoCodec, initSegment: duration ? initSegment : null };
        }
        this.observer.trigger(_events2.default.FRAG_PARSING_INIT_SEGMENT, { tracks: tracks });
      } else {
        if (audioCodec) {
          this.audioCodec = audioCodec;
        }
        if (videoCodec) {
          this.videoCodec = videoCodec;
        }
      }
    }
  }, {
    key: 'append',


    // feed incoming data to the front of the parsing pipeline
    value: function append(data, timeOffset, contiguous, accurateTimeOffset) {
      var initData = this.initData;
      if (!initData) {
        this.resetInitSegment(data, this.audioCodec, this.videoCodec);
        initData = this.initData;
      }
      var startDTS = void 0,
          initPTS = this.initPTS;
      if (initPTS === undefined) {
        var _startDTS = MP4Demuxer.getStartDTS(initData, data);
        this.initPTS = initPTS = _startDTS - timeOffset;
        this.observer.trigger(_events2.default.INIT_PTS_FOUND, { initPTS: initPTS });
      }
      MP4Demuxer.offsetStartDTS(initData, data, initPTS);
      startDTS = MP4Demuxer.getStartDTS(initData, data);
      this.remuxer.remux(initData.audio, initData.video, null, null, startDTS, contiguous, accurateTimeOffset, data);
    }
  }, {
    key: 'destroy',
    value: function destroy() {}
  }], [{
    key: 'probe',
    value: function probe(data) {
      if (data.length >= 8) {
        var dataType = MP4Demuxer.bin2str(data.subarray(4, 8));
        return ['moof', 'ftyp', 'styp'].indexOf(dataType) >= 0;
      }
      return false;
    }
  }, {
    key: 'bin2str',
    value: function bin2str(buffer) {
      return String.fromCharCode.apply(null, buffer);
    }
  }, {
    key: 'readUint32',
    value: function readUint32(buffer, offset) {
      if (buffer.data) {
        offset += buffer.start;
        buffer = buffer.data;
      }

      var val = buffer[offset] << 24 | buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3];
      return val < 0 ? 4294967296 + val : val;
    }
  }, {
    key: 'writeUint32',
    value: function writeUint32(buffer, offset, value) {
      if (buffer.data) {
        offset += buffer.start;
        buffer = buffer.data;
      }
      buffer[offset] = value >> 24;
      buffer[offset + 1] = value >> 16 & 0xff;
      buffer[offset + 2] = value >> 8 & 0xff;
      buffer[offset + 3] = value & 0xff;
    }

    // Find the data for a box specified by its path

  }, {
    key: 'findBox',
    value: function findBox(data, path) {
      var results = [],
          i,
          size,
          type,
          end,
          subresults,
          start,
          endbox;

      if (data.data) {
        start = data.start;
        end = data.end;
        data = data.data;
      } else {
        start = 0;
        end = data.byteLength;
      }

      if (!path.length) {
        // short-circuit the search for empty paths
        return null;
      }

      for (i = start; i < end;) {
        size = MP4Demuxer.readUint32(data, i);
        type = MP4Demuxer.bin2str(data.subarray(i + 4, i + 8));
        endbox = size > 1 ? i + size : end;

        if (type === path[0]) {

          if (path.length === 1) {
            // this is the end of the path and we've found the box we were
            // looking for
            results.push({ data: data, start: i + 8, end: endbox });
          } else {
            // recursively search for the next box along the path
            subresults = MP4Demuxer.findBox({ data: data, start: i + 8, end: endbox }, path.slice(1));
            if (subresults.length) {
              results = results.concat(subresults);
            }
          }
        }
        i = endbox;
      }

      // we've finished searching all of data
      return results;
    }

    /**
     * Parses an MP4 initialization segment and extracts stream type and
     * timescale values for any declared tracks. Timescale values indicate the
     * number of clock ticks per second to assume for time-based values
     * elsewhere in the MP4.
     *
     * To determine the start time of an MP4, you need two pieces of
     * information: the timescale unit and the earliest base media decode
     * time. Multiple timescales can be specified within an MP4 but the
     * base media decode time is always expressed in the timescale from
     * the media header box for the track:
     * ```
     * moov > trak > mdia > mdhd.timescale
     * moov > trak > mdia > hdlr
     * ```
     * @param init {Uint8Array} the bytes of the init segment
     * @return {object} a hash of track type to timescale values or null if
     * the init segment is malformed.
     */

  }, {
    key: 'parseInitSegment',
    value: function parseInitSegment(initSegment) {
      var result = [];
      var traks = MP4Demuxer.findBox(initSegment, ['moov', 'trak']);

      traks.forEach(function (trak) {
        var tkhd = MP4Demuxer.findBox(trak, ['tkhd'])[0];
        if (tkhd) {
          var version = tkhd.data[tkhd.start];
          var index = version === 0 ? 12 : 20;
          var trackId = MP4Demuxer.readUint32(tkhd, index);

          var mdhd = MP4Demuxer.findBox(trak, ['mdia', 'mdhd'])[0];
          if (mdhd) {
            version = mdhd.data[mdhd.start];
            index = version === 0 ? 12 : 20;
            var timescale = MP4Demuxer.readUint32(mdhd, index);

            var hdlr = MP4Demuxer.findBox(trak, ['mdia', 'hdlr'])[0];
            if (hdlr) {
              var hdlrType = MP4Demuxer.bin2str(hdlr.data.subarray(hdlr.start + 8, hdlr.start + 12));
              var type = { 'soun': 'audio', 'vide': 'video' }[hdlrType];
              if (type) {
                result[trackId] = { timescale: timescale, type: type };
                result[type] = { timescale: timescale, id: trackId };
              }
            }
          }
        }
      });
      return result;
    }

    /**
     * Determine the base media decode start time, in seconds, for an MP4
     * fragment. If multiple fragments are specified, the earliest time is
     * returned.
     *
     * The base media decode time can be parsed from track fragment
     * metadata:
     * ```
     * moof > traf > tfdt.baseMediaDecodeTime
     * ```
     * It requires the timescale value from the mdhd to interpret.
     *
     * @param timescale {object} a hash of track ids to timescale values.
     * @return {number} the earliest base media decode start time for the
     * fragment, in seconds
     */

  }, {
    key: 'getStartDTS',
    value: function getStartDTS(initData, fragment) {
      var trafs, baseTimes, result;

      // we need info from two childrend of each track fragment box
      trafs = MP4Demuxer.findBox(fragment, ['moof', 'traf']);

      // determine the start times for each track
      baseTimes = [].concat.apply([], trafs.map(function (traf) {
        return MP4Demuxer.findBox(traf, ['tfhd']).map(function (tfhd) {
          var id, scale, baseTime;

          // get the track id from the tfhd
          id = MP4Demuxer.readUint32(tfhd, 4);
          // assume a 90kHz clock if no timescale was specified
          scale = initData[id].timescale || 90e3;

          // get the base media decode time from the tfdt
          baseTime = MP4Demuxer.findBox(traf, ['tfdt']).map(function (tfdt) {
            var version, result;

            version = tfdt.data[tfdt.start];
            result = MP4Demuxer.readUint32(tfdt, 4);
            if (version === 1) {
              result *= Math.pow(2, 32);

              result += MP4Demuxer.readUint32(tfdt, 8);
            }
            return result;
          })[0];
          baseTime = baseTime || Infinity;

          // convert base time to seconds
          return baseTime / scale;
        });
      }));

      // return the minimum
      result = Math.min.apply(null, baseTimes);
      return isFinite(result) ? result : 0;
    }
  }, {
    key: 'offsetStartDTS',
    value: function offsetStartDTS(initData, fragment, timeOffset) {
      MP4Demuxer.findBox(fragment, ['moof', 'traf']).map(function (traf) {
        return MP4Demuxer.findBox(traf, ['tfhd']).map(function (tfhd) {
          // get the track id from the tfhd
          var id = MP4Demuxer.readUint32(tfhd, 4);
          // assume a 90kHz clock if no timescale was specified
          var timescale = initData[id].timescale || 90e3;

          // get the base media decode time from the tfdt
          MP4Demuxer.findBox(traf, ['tfdt']).map(function (tfdt) {
            var version = tfdt.data[tfdt.start];
            var baseMediaDecodeTime = MP4Demuxer.readUint32(tfdt, 4);
            if (version === 0) {
              MP4Demuxer.writeUint32(tfdt, 4, baseMediaDecodeTime - timeOffset * timescale);
            } else {
              baseMediaDecodeTime *= Math.pow(2, 32);
              baseMediaDecodeTime += MP4Demuxer.readUint32(tfdt, 8);
              baseMediaDecodeTime -= timeOffset * timescale;
              var upper = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1));
              var lower = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1));
              MP4Demuxer.writeUint32(tfdt, 4, upper);
              MP4Demuxer.writeUint32(tfdt, 8, lower);
            }
          });
        });
      });
    }
  }]);

  return MP4Demuxer;
}();

exports.default = MP4Demuxer;

},{"35":35}],30:[function(_dereq_,module,exports){
"use strict";

/**
 *  MPEG parser helper
 */

var MpegAudio = {

    BitratesMap: [32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160],

    SamplingRateMap: [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000],

    appendFrame: function appendFrame(track, data, offset, pts, frameIndex) {
        // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference
        if (offset + 24 > data.length) {
            return undefined;
        }

        var header = this.parseHeader(data, offset);
        if (header && offset + header.frameLength <= data.length) {
            var frameDuration = 1152 * 90000 / header.sampleRate;
            var stamp = pts + frameIndex * frameDuration;
            var sample = { unit: data.subarray(offset, offset + header.frameLength), pts: stamp, dts: stamp };

            track.config = [];
            track.channelCount = header.channelCount;
            track.samplerate = header.sampleRate;
            track.samples.push(sample);
            track.len += header.frameLength;

            return { sample: sample, length: header.frameLength };
        }

        return undefined;
    },

    parseHeader: function parseHeader(data, offset) {
        var headerB = data[offset + 1] >> 3 & 3;
        var headerC = data[offset + 1] >> 1 & 3;
        var headerE = data[offset + 2] >> 4 & 15;
        var headerF = data[offset + 2] >> 2 & 3;
        var headerG = !!(data[offset + 2] & 2);
        if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) {
            var columnInBitrates = headerB === 3 ? 3 - headerC : headerC === 3 ? 3 : 4;
            var bitRate = MpegAudio.BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000;
            var columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2;
            var sampleRate = MpegAudio.SamplingRateMap[columnInSampleRates * 3 + headerF];
            var padding = headerG ? 1 : 0;
            var channelCount = data[offset + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono)
            var frameLength = headerC === 3 ? (headerB === 3 ? 12 : 6) * bitRate / sampleRate + padding << 2 : (headerB === 3 ? 144 : 72) * bitRate / sampleRate + padding | 0;

            return { sampleRate: sampleRate, channelCount: channelCount, frameLength: frameLength };
        }

        return undefined;
    },

    isHeaderPattern: function isHeaderPattern(data, offset) {
        return data[offset] === 0xff && (data[offset + 1] & 0xe0) === 0xe0 && (data[offset + 1] & 0x06) !== 0x00;
    },

    isHeader: function isHeader(data, offset) {
        // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1
        // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III)
        // More info http://www.mp3-tech.org/programmer/frame_header.html
        if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) {
            return true;
        }
        return false;
    },

    probe: function probe(data, offset) {
        // same as isHeader but we also check that MPEG frame follows last MPEG frame 
        // or end of data is reached
        if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) {
            // MPEG header Length
            var headerLength = 4;
            // MPEG frame Length
            var header = this.parseHeader(data, offset);
            var frameLength = headerLength;
            if (header && header.frameLength) {
                frameLength = header.frameLength;
            }
            var newOffset = offset + frameLength;
            if (newOffset === data.length || newOffset + 1 < data.length && this.isHeaderPattern(data, newOffset)) {
                return true;
            }
        }
        return false;
    }
};

module.exports = MpegAudio;

},{}],31:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }(); /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * SAMPLE-AES decrypter
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     */

var _decrypter = _dereq_(19);

var _decrypter2 = _interopRequireDefault(_decrypter);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var SampleAesDecrypter = function () {
  function SampleAesDecrypter(observer, config, decryptdata, discardEPB) {
    _classCallCheck(this, SampleAesDecrypter);

    this.decryptdata = decryptdata;
    this.discardEPB = discardEPB;
    this.decrypter = new _decrypter2.default(observer, config);
  }

  _createClass(SampleAesDecrypter, [{
    key: 'decryptBuffer',
    value: function decryptBuffer(encryptedData, callback) {
      this.decrypter.decrypt(encryptedData, this.decryptdata.key.buffer, this.decryptdata.iv.buffer, callback);
    }

    // AAC - encrypt all full 16 bytes blocks starting from offset 16

  }, {
    key: 'decryptAacSample',
    value: function decryptAacSample(samples, sampleIndex, callback, sync) {
      var curUnit = samples[sampleIndex].unit;
      var encryptedData = curUnit.subarray(16, curUnit.length - curUnit.length % 16);
      var encryptedBuffer = encryptedData.buffer.slice(encryptedData.byteOffset, encryptedData.byteOffset + encryptedData.length);

      var localthis = this;
      this.decryptBuffer(encryptedBuffer, function (decryptedData) {
        decryptedData = new Uint8Array(decryptedData);
        curUnit.set(decryptedData, 16);

        if (!sync) {
          localthis.decryptAacSamples(samples, sampleIndex + 1, callback);
        }
      });
    }
  }, {
    key: 'decryptAacSamples',
    value: function decryptAacSamples(samples, sampleIndex, callback) {
      for (;; sampleIndex++) {
        if (sampleIndex >= samples.length) {
          callback();
          return;
        }

        if (samples[sampleIndex].unit.length < 32) {
          continue;
        }

        var sync = this.decrypter.isSync();

        this.decryptAacSample(samples, sampleIndex, callback, sync);

        if (!sync) {
          return;
        }
      }
    }

    // AVC - encrypt one 16 bytes block out of ten, starting from offset 32

  }, {
    key: 'getAvcEncryptedData',
    value: function getAvcEncryptedData(decodedData) {
      var encryptedDataLen = Math.floor((decodedData.length - 48) / 160) * 16 + 16;
      var encryptedData = new Int8Array(encryptedDataLen);
      var outputPos = 0;
      for (var inputPos = 32; inputPos <= decodedData.length - 16; inputPos += 160, outputPos += 16) {
        encryptedData.set(decodedData.subarray(inputPos, inputPos + 16), outputPos);
      }
      return encryptedData;
    }
  }, {
    key: 'getAvcDecryptedUnit',
    value: function getAvcDecryptedUnit(decodedData, decryptedData) {
      decryptedData = new Uint8Array(decryptedData);
      var inputPos = 0;
      for (var outputPos = 32; outputPos <= decodedData.length - 16; outputPos += 160, inputPos += 16) {
        decodedData.set(decryptedData.subarray(inputPos, inputPos + 16), outputPos);
      }
      return decodedData;
    }
  }, {
    key: 'decryptAvcSample',
    value: function decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit, sync) {
      var decodedData = this.discardEPB(curUnit.data);
      var encryptedData = this.getAvcEncryptedData(decodedData);
      var localthis = this;

      this.decryptBuffer(encryptedData.buffer, function (decryptedData) {
        curUnit.data = localthis.getAvcDecryptedUnit(decodedData, decryptedData);

        if (!sync) {
          localthis.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback);
        }
      });
    }
  }, {
    key: 'decryptAvcSamples',
    value: function decryptAvcSamples(samples, sampleIndex, unitIndex, callback) {
      for (;; sampleIndex++, unitIndex = 0) {
        if (sampleIndex >= samples.length) {
          callback();
          return;
        }

        var curUnits = samples[sampleIndex].units;
        for (;; unitIndex++) {
          if (unitIndex >= curUnits.length) {
            break;
          }

          var curUnit = curUnits[unitIndex];
          if (curUnit.length <= 48 || curUnit.type !== 1 && curUnit.type !== 5) {
            continue;
          }

          var sync = this.decrypter.isSync();

          this.decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit, sync);

          if (!sync) {
            return;
          }
        }
      }
    }
  }]);

  return SampleAesDecrypter;
}();

exports.default = SampleAesDecrypter;

},{"19":19}],32:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }(); /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * highly optimized TS demuxer:
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * parse PAT, PMT
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * extract PES packet from audio and video PIDs
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * extract AVC/H264 NAL units and AAC/ADTS samples from PES packet
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * trigger the remuxer upon parsing completion
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * it also tries to workaround as best as it can audio codec switch (HE-AAC to AAC and vice versa), without having to restart the MediaSource.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * it also controls the remuxing process :
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * upon discontinuity or level switch detection, it will also notifies the remuxer so that it can reset its state.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     */

// import Hex from '../utils/hex';


var _adts = _dereq_(22);

var _adts2 = _interopRequireDefault(_adts);

var _mpegaudio = _dereq_(30);

var _mpegaudio2 = _interopRequireDefault(_mpegaudio);

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _expGolomb = _dereq_(26);

var _expGolomb2 = _interopRequireDefault(_expGolomb);

var _sampleAes = _dereq_(31);

var _sampleAes2 = _interopRequireDefault(_sampleAes);

var _logger = _dereq_(54);

var _errors = _dereq_(33);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var TSDemuxer = function () {
  function TSDemuxer(observer, remuxer, config, typeSupported) {
    _classCallCheck(this, TSDemuxer);

    this.observer = observer;
    this.config = config;
    this.typeSupported = typeSupported;
    this.remuxer = remuxer;
    this.sampleAes = null;
  }

  _createClass(TSDemuxer, [{
    key: 'setDecryptData',
    value: function setDecryptData(decryptdata) {
      if (decryptdata != null && decryptdata.key != null && decryptdata.method === 'SAMPLE-AES') {
        this.sampleAes = new _sampleAes2.default(this.observer, this.config, decryptdata, this.discardEPB);
      } else {
        this.sampleAes = null;
      }
    }
  }, {
    key: 'resetInitSegment',
    value: function resetInitSegment(initSegment, audioCodec, videoCodec, duration) {
      this.pmtParsed = false;
      this._pmtId = -1;
      this._avcTrack = { container: 'video/mp2t', type: 'video', id: -1, inputTimeScale: 90000, sequenceNumber: 0, samples: [], len: 0, dropped: 0 };
      this._audioTrack = { container: 'video/mp2t', type: 'audio', id: -1, inputTimeScale: 90000, duration: duration, sequenceNumber: 0, samples: [], len: 0, isAAC: true };
      this._id3Track = { type: 'id3', id: -1, inputTimeScale: 90000, sequenceNumber: 0, samples: [], len: 0 };
      this._txtTrack = { type: 'text', id: -1, inputTimeScale: 90000, sequenceNumber: 0, samples: [], len: 0 };
      // flush any partial content
      this.aacOverFlow = null;
      this.aacLastPTS = null;
      this.avcSample = null;
      this.audioCodec = audioCodec;
      this.videoCodec = videoCodec;
      this._duration = duration;
    }
  }, {
    key: 'resetTimeStamp',
    value: function resetTimeStamp() {}

    // feed incoming data to the front of the parsing pipeline

  }, {
    key: 'append',
    value: function append(data, timeOffset, contiguous, accurateTimeOffset) {
      var start,
          len = data.length,
          stt,
          pid,
          atf,
          offset,
          pes,
          unknownPIDs = false;
      this.contiguous = contiguous;
      var pmtParsed = this.pmtParsed,
          avcTrack = this._avcTrack,
          audioTrack = this._audioTrack,
          id3Track = this._id3Track,
          avcId = avcTrack.id,
          audioId = audioTrack.id,
          id3Id = id3Track.id,
          pmtId = this._pmtId,
          avcData = avcTrack.pesData,
          audioData = audioTrack.pesData,
          id3Data = id3Track.pesData,
          parsePAT = this._parsePAT,
          parsePMT = this._parsePMT,
          parsePES = this._parsePES,
          parseAVCPES = this._parseAVCPES.bind(this),
          parseAACPES = this._parseAACPES.bind(this),
          parseMPEGPES = this._parseMPEGPES.bind(this),
          parseID3PES = this._parseID3PES.bind(this);

      // don't parse last TS packet if incomplete
      len -= len % 188;
      // loop through TS packets
      for (start = 0; start < len; start += 188) {
        if (data[start] === 0x47) {
          stt = !!(data[start + 1] & 0x40);
          // pid is a 13-bit field starting at the last bit of TS[1]
          pid = ((data[start + 1] & 0x1f) << 8) + data[start + 2];
          atf = (data[start + 3] & 0x30) >> 4;
          // if an adaption field is present, its length is specified by the fifth byte of the TS packet header.
          if (atf > 1) {
            offset = start + 5 + data[start + 4];
            // continue if there is only adaptation field
            if (offset === start + 188) {
              continue;
            }
          } else {
            offset = start + 4;
          }
          switch (pid) {
            case avcId:
              if (stt) {
                if (avcData && (pes = parsePES(avcData))) {
                  parseAVCPES(pes, false);
                }
                avcData = { data: [], size: 0 };
              }
              if (avcData) {
                avcData.data.push(data.subarray(offset, start + 188));
                avcData.size += start + 188 - offset;
              }
              break;
            case audioId:
              if (stt) {
                if (audioData && (pes = parsePES(audioData))) {
                  if (audioTrack.isAAC) {
                    parseAACPES(pes);
                  } else {
                    parseMPEGPES(pes);
                  }
                }
                audioData = { data: [], size: 0 };
              }
              if (audioData) {
                audioData.data.push(data.subarray(offset, start + 188));
                audioData.size += start + 188 - offset;
              }
              break;
            case id3Id:
              if (stt) {
                if (id3Data && (pes = parsePES(id3Data))) {
                  parseID3PES(pes);
                }
                id3Data = { data: [], size: 0 };
              }
              if (id3Data) {
                id3Data.data.push(data.subarray(offset, start + 188));
                id3Data.size += start + 188 - offset;
              }
              break;
            case 0:
              if (stt) {
                offset += data[offset] + 1;
              }
              pmtId = this._pmtId = parsePAT(data, offset);
              break;
            case pmtId:
              if (stt) {
                offset += data[offset] + 1;
              }
              var parsedPIDs = parsePMT(data, offset, this.typeSupported.mpeg === true || this.typeSupported.mp3 === true, this.sampleAes != null);

              // only update track id if track PID found while parsing PMT
              // this is to avoid resetting the PID to -1 in case
              // track PID transiently disappears from the stream
              // this could happen in case of transient missing audio samples for example
              avcId = parsedPIDs.avc;
              if (avcId > 0) {
                avcTrack.id = avcId;
              }
              audioId = parsedPIDs.audio;
              if (audioId > 0) {
                audioTrack.id = audioId;
                audioTrack.isAAC = parsedPIDs.isAAC;
              }
              id3Id = parsedPIDs.id3;
              if (id3Id > 0) {
                id3Track.id = id3Id;
              }
              if (unknownPIDs && !pmtParsed) {
                _logger.logger.log('reparse from beginning');
                unknownPIDs = false;
                // we set it to -188, the += 188 in the for loop will reset start to 0
                start = -188;
              }
              pmtParsed = this.pmtParsed = true;
              break;
            case 17:
            case 0x1fff:
              break;
            default:
              unknownPIDs = true;
              break;
          }
        } else {
          this.observer.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.FRAG_PARSING_ERROR, fatal: false, reason: 'TS packet did not start with 0x47' });
        }
      }
      // try to parse last PES packets
      if (avcData && (pes = parsePES(avcData))) {
        parseAVCPES(pes, true);
        avcTrack.pesData = null;
      } else {
        // either avcData null or PES truncated, keep it for next frag parsing
        avcTrack.pesData = avcData;
      }

      if (audioData && (pes = parsePES(audioData))) {
        if (audioTrack.isAAC) {
          parseAACPES(pes);
        } else {
          parseMPEGPES(pes);
        }
        audioTrack.pesData = null;
      } else {
        if (audioData && audioData.size) {
          _logger.logger.log('last AAC PES packet truncated,might overlap between fragments');
        }
        // either audioData null or PES truncated, keep it for next frag parsing
        audioTrack.pesData = audioData;
      }

      if (id3Data && (pes = parsePES(id3Data))) {
        parseID3PES(pes);
        id3Track.pesData = null;
      } else {
        // either id3Data null or PES truncated, keep it for next frag parsing
        id3Track.pesData = id3Data;
      }

      if (this.sampleAes == null) {
        this.remuxer.remux(audioTrack, avcTrack, id3Track, this._txtTrack, timeOffset, contiguous, accurateTimeOffset);
      } else {
        this.decryptAndRemux(audioTrack, avcTrack, id3Track, this._txtTrack, timeOffset, contiguous, accurateTimeOffset);
      }
    }
  }, {
    key: 'decryptAndRemux',
    value: function decryptAndRemux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) {
      if (audioTrack.samples && audioTrack.isAAC) {
        var localthis = this;
        this.sampleAes.decryptAacSamples(audioTrack.samples, 0, function () {
          localthis.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
        });
      } else {
        this.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
      }
    }
  }, {
    key: 'decryptAndRemuxAvc',
    value: function decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) {
      if (videoTrack.samples) {
        var localthis = this;
        this.sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, function () {
          localthis.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
        });
      } else {
        this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset);
      }
    }
  }, {
    key: 'destroy',
    value: function destroy() {
      this._initPTS = this._initDTS = undefined;
      this._duration = 0;
    }
  }, {
    key: '_parsePAT',
    value: function _parsePAT(data, offset) {
      // skip the PSI header and parse the first PMT entry
      return (data[offset + 10] & 0x1F) << 8 | data[offset + 11];
      //logger.log('PMT PID:'  + this._pmtId);
    }
  }, {
    key: '_parsePMT',
    value: function _parsePMT(data, offset, mpegSupported, isSampleAes) {
      var sectionLength,
          tableEnd,
          programInfoLength,
          pid,
          result = { audio: -1, avc: -1, id3: -1, isAAC: true };
      sectionLength = (data[offset + 1] & 0x0f) << 8 | data[offset + 2];
      tableEnd = offset + 3 + sectionLength - 4;
      // to determine where the table is, we have to figure out how
      // long the program info descriptors are
      programInfoLength = (data[offset + 10] & 0x0f) << 8 | data[offset + 11];
      // advance the offset to the first entry in the mapping table
      offset += 12 + programInfoLength;
      while (offset < tableEnd) {
        pid = (data[offset + 1] & 0x1F) << 8 | data[offset + 2];
        switch (data[offset]) {
          case 0xcf:
            // SAMPLE-AES AAC
            if (!isSampleAes) {
              _logger.logger.log('unkown stream type:' + data[offset]);
              break;
            }
          /* falls through */

          // ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio)
          case 0x0f:
            //logger.log('AAC PID:'  + pid);
            if (result.audio === -1) {
              result.audio = pid;
            }
            break;

          // Packetized metadata (ID3)
          case 0x15:
            //logger.log('ID3 PID:'  + pid);
            if (result.id3 === -1) {
              result.id3 = pid;
            }
            break;

          case 0xdb:
            // SAMPLE-AES AVC
            if (!isSampleAes) {
              _logger.logger.log('unkown stream type:' + data[offset]);
              break;
            }
          /* falls through */

          // ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video)
          case 0x1b:
            //logger.log('AVC PID:'  + pid);
            if (result.avc === -1) {
              result.avc = pid;
            }
            break;

          // ISO/IEC 11172-3 (MPEG-1 audio)
          // or ISO/IEC 13818-3 (MPEG-2 halved sample rate audio)
          case 0x03:
          case 0x04:
            //logger.log('MPEG PID:'  + pid);
            if (!mpegSupported) {
              _logger.logger.log('MPEG audio found, not supported in this browser for now');
            } else if (result.audio === -1) {
              result.audio = pid;
              result.isAAC = false;
            }
            break;

          case 0x24:
            _logger.logger.warn('HEVC stream type found, not supported for now');
            break;

          default:
            _logger.logger.log('unkown stream type:' + data[offset]);
            break;
        }
        // move to the next table entry
        // skip past the elementary stream descriptors, if present
        offset += ((data[offset + 3] & 0x0F) << 8 | data[offset + 4]) + 5;
      }
      return result;
    }
  }, {
    key: '_parsePES',
    value: function _parsePES(stream) {
      var i = 0,
          frag,
          pesFlags,
          pesPrefix,
          pesLen,
          pesHdrLen,
          pesData,
          pesPts,
          pesDts,
          payloadStartOffset,
          data = stream.data;
      // safety check
      if (!stream || stream.size === 0) {
        return null;
      }

      // we might need up to 19 bytes to read PES header
      // if first chunk of data is less than 19 bytes, let's merge it with following ones until we get 19 bytes
      // usually only one merge is needed (and this is rare ...)
      while (data[0].length < 19 && data.length > 1) {
        var newData = new Uint8Array(data[0].length + data[1].length);
        newData.set(data[0]);
        newData.set(data[1], data[0].length);
        data[0] = newData;
        data.splice(1, 1);
      }
      //retrieve PTS/DTS from first fragment
      frag = data[0];
      pesPrefix = (frag[0] << 16) + (frag[1] << 8) + frag[2];
      if (pesPrefix === 1) {
        pesLen = (frag[4] << 8) + frag[5];
        // if PES parsed length is not zero and greater than total received length, stop parsing. PES might be truncated
        // minus 6 : PES header size
        if (pesLen && pesLen > stream.size - 6) {
          return null;
        }
        pesFlags = frag[7];
        if (pesFlags & 0xC0) {
          /* PES header described here : http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
              as PTS / DTS is 33 bit we cannot use bitwise operator in JS,
              as Bitwise operators treat their operands as a sequence of 32 bits */
          pesPts = (frag[9] & 0x0E) * 536870912 + // 1 << 29
          (frag[10] & 0xFF) * 4194304 + // 1 << 22
          (frag[11] & 0xFE) * 16384 + // 1 << 14
          (frag[12] & 0xFF) * 128 + // 1 << 7
          (frag[13] & 0xFE) / 2;
          // check if greater than 2^32 -1
          if (pesPts > 4294967295) {
            // decrement 2^33
            pesPts -= 8589934592;
          }
          if (pesFlags & 0x40) {
            pesDts = (frag[14] & 0x0E) * 536870912 + // 1 << 29
            (frag[15] & 0xFF) * 4194304 + // 1 << 22
            (frag[16] & 0xFE) * 16384 + // 1 << 14
            (frag[17] & 0xFF) * 128 + // 1 << 7
            (frag[18] & 0xFE) / 2;
            // check if greater than 2^32 -1
            if (pesDts > 4294967295) {
              // decrement 2^33
              pesDts -= 8589934592;
            }
            if (pesPts - pesDts > 60 * 90000) {
              _logger.logger.warn(Math.round((pesPts - pesDts) / 90000) + 's delta between PTS and DTS, align them');
              pesPts = pesDts;
            }
          } else {
            pesDts = pesPts;
          }
        }
        pesHdrLen = frag[8];
        // 9 bytes : 6 bytes for PES header + 3 bytes for PES extension
        payloadStartOffset = pesHdrLen + 9;

        stream.size -= payloadStartOffset;
        //reassemble PES packet
        pesData = new Uint8Array(stream.size);
        for (var j = 0, dataLen = data.length; j < dataLen; j++) {
          frag = data[j];
          var len = frag.byteLength;
          if (payloadStartOffset) {
            if (payloadStartOffset > len) {
              // trim full frag if PES header bigger than frag
              payloadStartOffset -= len;
              continue;
            } else {
              // trim partial frag if PES header smaller than frag
              frag = frag.subarray(payloadStartOffset);
              len -= payloadStartOffset;
              payloadStartOffset = 0;
            }
          }
          pesData.set(frag, i);
          i += len;
        }
        if (pesLen) {
          // payload size : remove PES header + PES extension
          pesLen -= pesHdrLen + 3;
        }
        return { data: pesData, pts: pesPts, dts: pesDts, len: pesLen };
      } else {
        return null;
      }
    }
  }, {
    key: 'pushAccesUnit',
    value: function pushAccesUnit(avcSample, avcTrack) {
      if (avcSample.units.length && avcSample.frame) {
        var samples = avcTrack.samples;
        var nbSamples = samples.length;
        // only push AVC sample if starting with a keyframe is not mandatory OR
        //    if keyframe already found in this fragment OR
        //       keyframe found in last fragment (track.sps) AND
        //          samples already appended (we already found a keyframe in this fragment) OR fragment is contiguous
        if (!this.config.forceKeyFrameOnDiscontinuity || avcSample.key === true || avcTrack.sps && (nbSamples || this.contiguous)) {
          avcSample.id = nbSamples;
          samples.push(avcSample);
        } else {
          // dropped samples, track it
          avcTrack.dropped++;
        }
      }
      if (avcSample.debug.length) {
        _logger.logger.log(avcSample.pts + '/' + avcSample.dts + ':' + avcSample.debug);
      }
    }
  }, {
    key: '_parseAVCPES',
    value: function _parseAVCPES(pes, last) {
      var _this = this;

      //logger.log('parse new PES');
      var track = this._avcTrack,
          units = this._parseAVCNALu(pes.data),
          debug = false,
          expGolombDecoder,
          avcSample = this.avcSample,
          push,
          spsfound = false,
          i,
          pushAccesUnit = this.pushAccesUnit.bind(this),
          createAVCSample = function createAVCSample(key, pts, dts, debug) {
        return { key: key, pts: pts, dts: dts, units: [], debug: debug };
      };
      //free pes.data to save up some memory
      pes.data = null;

      // if new NAL units found and last sample still there, let's push ...
      // this helps parsing streams with missing AUD
      if (avcSample && units.length) {
        pushAccesUnit(avcSample, track);
        avcSample = this.avcSample = createAVCSample(false, pes.pts, pes.dts, '');
      }

      units.forEach(function (unit) {
        switch (unit.type) {
          //NDR
          case 1:
            push = true;
            if (debug && avcSample) {
              avcSample.debug += 'NDR ';
            }
            avcSample.frame = true;
            var data = unit.data;
            // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
            if (spsfound && data.length > 4) {
              // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
              var sliceType = new _expGolomb2.default(data).readSliceType();
              // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
              // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
              // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
              // I slice: A slice that is not an SI slice that is decoded using intra prediction only.
              //if (sliceType === 2 || sliceType === 7) {
              if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) {
                avcSample.key = true;
              }
            }
            break;
          //IDR
          case 5:
            push = true;
            // handle PES not starting with AUD
            if (!avcSample) {
              avcSample = _this.avcSample = createAVCSample(true, pes.pts, pes.dts, '');
            }
            if (debug) {
              avcSample.debug += 'IDR ';
            }
            avcSample.key = true;
            avcSample.frame = true;
            break;
          //SEI
          case 6:
            push = true;
            if (debug && avcSample) {
              avcSample.debug += 'SEI ';
            }
            expGolombDecoder = new _expGolomb2.default(_this.discardEPB(unit.data));

            // skip frameType
            expGolombDecoder.readUByte();

            var payloadType = 0;
            var payloadSize = 0;
            var endOfCaptions = false;
            var b = 0;

            while (!endOfCaptions && expGolombDecoder.bytesAvailable > 1) {
              payloadType = 0;
              do {
                b = expGolombDecoder.readUByte();
                payloadType += b;
              } while (b === 0xFF);

              // Parse payload size.
              payloadSize = 0;
              do {
                b = expGolombDecoder.readUByte();
                payloadSize += b;
              } while (b === 0xFF);

              // TODO: there can be more than one payload in an SEI packet...
              // TODO: need to read type and size in a while loop to get them all
              if (payloadType === 4 && expGolombDecoder.bytesAvailable !== 0) {

                endOfCaptions = true;

                var countryCode = expGolombDecoder.readUByte();

                if (countryCode === 181) {
                  var providerCode = expGolombDecoder.readUShort();

                  if (providerCode === 49) {
                    var userStructure = expGolombDecoder.readUInt();

                    if (userStructure === 0x47413934) {
                      var userDataType = expGolombDecoder.readUByte();

                      // Raw CEA-608 bytes wrapped in CEA-708 packet
                      if (userDataType === 3) {
                        var firstByte = expGolombDecoder.readUByte();
                        var secondByte = expGolombDecoder.readUByte();

                        var totalCCs = 31 & firstByte;
                        var byteArray = [firstByte, secondByte];

                        for (i = 0; i < totalCCs; i++) {
                          // 3 bytes per CC
                          byteArray.push(expGolombDecoder.readUByte());
                          byteArray.push(expGolombDecoder.readUByte());
                          byteArray.push(expGolombDecoder.readUByte());
                        }

                        _this._insertSampleInOrder(_this._txtTrack.samples, { type: 3, pts: pes.pts, bytes: byteArray });
                      }
                    }
                  }
                }
              } else if (payloadSize < expGolombDecoder.bytesAvailable) {
                for (i = 0; i < payloadSize; i++) {
                  expGolombDecoder.readUByte();
                }
              }
            }
            break;
          //SPS
          case 7:
            push = true;
            spsfound = true;
            if (debug && avcSample) {
              avcSample.debug += 'SPS ';
            }
            if (!track.sps) {
              expGolombDecoder = new _expGolomb2.default(unit.data);
              var config = expGolombDecoder.readSPS();
              track.width = config.width;
              track.height = config.height;
              track.pixelRatio = config.pixelRatio;
              track.sps = [unit.data];
              track.duration = _this._duration;
              var codecarray = unit.data.subarray(1, 4);
              var codecstring = 'avc1.';
              for (i = 0; i < 3; i++) {
                var h = codecarray[i].toString(16);
                if (h.length < 2) {
                  h = '0' + h;
                }
                codecstring += h;
              }
              track.codec = codecstring;
            }
            break;
          //PPS
          case 8:
            push = true;
            if (debug && avcSample) {
              avcSample.debug += 'PPS ';
            }
            if (!track.pps) {
              track.pps = [unit.data];
            }
            break;
          // AUD
          case 9:
            push = false;
            if (avcSample) {
              pushAccesUnit(avcSample, track);
            }
            avcSample = _this.avcSample = createAVCSample(false, pes.pts, pes.dts, debug ? 'AUD ' : '');
            break;
          // Filler Data
          case 12:
            push = false;
            break;
          default:
            push = false;
            if (avcSample) {
              avcSample.debug += 'unknown NAL ' + unit.type + ' ';
            }
            break;
        }
        if (avcSample && push) {
          var _units = avcSample.units;
          _units.push(unit);
        }
      });
      // if last PES packet, push samples
      if (last && avcSample) {
        pushAccesUnit(avcSample, track);
        this.avcSample = null;
      }
    }
  }, {
    key: '_insertSampleInOrder',
    value: function _insertSampleInOrder(arr, data) {
      var len = arr.length;
      if (len > 0) {
        if (data.pts >= arr[len - 1].pts) {
          arr.push(data);
        } else {
          for (var pos = len - 1; pos >= 0; pos--) {
            if (data.pts < arr[pos].pts) {
              arr.splice(pos, 0, data);
              break;
            }
          }
        }
      } else {
        arr.push(data);
      }
    }
  }, {
    key: '_getLastNalUnit',
    value: function _getLastNalUnit() {
      var avcSample = this.avcSample,
          lastUnit = void 0;
      // try to fallback to previous sample if current one is empty
      if (!avcSample || avcSample.units.length === 0) {
        var track = this._avcTrack,
            samples = track.samples;
        avcSample = samples[samples.length - 1];
      }
      if (avcSample) {
        var units = avcSample.units;
        lastUnit = units[units.length - 1];
      }
      return lastUnit;
    }
  }, {
    key: '_parseAVCNALu',
    value: function _parseAVCNALu(array) {
      var i = 0,
          len = array.byteLength,
          value,
          overflow,
          track = this._avcTrack,
          state = track.naluState || 0,
          lastState = state;
      var units = [],
          unit,
          unitType,
          lastUnitStart = -1,
          lastUnitType;
      //logger.log('PES:' + Hex.hexDump(array));

      if (state === -1) {
        // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
        lastUnitStart = 0;
        // NALu type is value read from offset 0
        lastUnitType = array[0] & 0x1f;
        state = 0;
        i = 1;
      }

      while (i < len) {
        value = array[i++];
        // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
        if (!state) {
          state = value ? 0 : 1;
          continue;
        }
        if (state === 1) {
          state = value ? 0 : 2;
          continue;
        }
        // here we have state either equal to 2 or 3
        if (!value) {
          state = 3;
        } else if (value === 1) {
          if (lastUnitStart >= 0) {
            unit = { data: array.subarray(lastUnitStart, i - state - 1), type: lastUnitType };
            //logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
            units.push(unit);
          } else {
            // lastUnitStart is undefined => this is the first start code found in this PES packet
            // first check if start code delimiter is overlapping between 2 PES packets,
            // ie it started in last packet (lastState not zero)
            // and ended at the beginning of this PES packet (i <= 4 - lastState)
            var lastUnit = this._getLastNalUnit();
            if (lastUnit) {
              if (lastState && i <= 4 - lastState) {
                // start delimiter overlapping between PES packets
                // strip start delimiter bytes from the end of last NAL unit
                // check if lastUnit had a state different from zero
                if (lastUnit.state) {
                  // strip last bytes
                  lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
                }
              }
              // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
              overflow = i - state - 1;
              if (overflow > 0) {
                //logger.log('first NALU found with overflow:' + overflow);
                var tmp = new Uint8Array(lastUnit.data.byteLength + overflow);
                tmp.set(lastUnit.data, 0);
                tmp.set(array.subarray(0, overflow), lastUnit.data.byteLength);
                lastUnit.data = tmp;
              }
            }
          }
          // check if we can read unit type
          if (i < len) {
            unitType = array[i] & 0x1f;
            //logger.log('find NALU @ offset:' + i + ',type:' + unitType);
            lastUnitStart = i;
            lastUnitType = unitType;
            state = 0;
          } else {
            // not enough byte to read unit type. let's read it on next PES parsing
            state = -1;
          }
        } else {
          state = 0;
        }
      }
      if (lastUnitStart >= 0 && state >= 0) {
        unit = { data: array.subarray(lastUnitStart, len), type: lastUnitType, state: state };
        units.push(unit);
        //logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
      }
      // no NALu found
      if (units.length === 0) {
        // append pes.data to previous NAL unit
        var _lastUnit = this._getLastNalUnit();
        if (_lastUnit) {
          var _tmp = new Uint8Array(_lastUnit.data.byteLength + array.byteLength);
          _tmp.set(_lastUnit.data, 0);
          _tmp.set(array, _lastUnit.data.byteLength);
          _lastUnit.data = _tmp;
        }
      }
      track.naluState = state;
      return units;
    }

    /**
     * remove Emulation Prevention bytes from a RBSP
     */

  }, {
    key: 'discardEPB',
    value: function discardEPB(data) {
      var length = data.byteLength,
          EPBPositions = [],
          i = 1,
          newLength,
          newData;

      // Find all `Emulation Prevention Bytes`
      while (i < length - 2) {
        if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) {
          EPBPositions.push(i + 2);
          i += 2;
        } else {
          i++;
        }
      }

      // If no Emulation Prevention Bytes were found just return the original
      // array
      if (EPBPositions.length === 0) {
        return data;
      }

      // Create a new array to hold the NAL unit data
      newLength = length - EPBPositions.length;
      newData = new Uint8Array(newLength);
      var sourceIndex = 0;

      for (i = 0; i < newLength; sourceIndex++, i++) {
        if (sourceIndex === EPBPositions[0]) {
          // Skip this byte
          sourceIndex++;
          // Remove this position index
          EPBPositions.shift();
        }
        newData[i] = data[sourceIndex];
      }
      return newData;
    }
  }, {
    key: '_parseAACPES',
    value: function _parseAACPES(pes) {
      var track = this._audioTrack,
          data = pes.data,
          pts = pes.pts,
          startOffset = 0,
          aacOverFlow = this.aacOverFlow,
          aacLastPTS = this.aacLastPTS,
          frameDuration,
          frameIndex,
          offset,
          stamp,
          len;
      if (aacOverFlow) {
        var tmp = new Uint8Array(aacOverFlow.byteLength + data.byteLength);
        tmp.set(aacOverFlow, 0);
        tmp.set(data, aacOverFlow.byteLength);
        //logger.log(`AAC: append overflowing ${aacOverFlow.byteLength} bytes to beginning of new PES`);
        data = tmp;
      }
      // look for ADTS header (0xFFFx)
      for (offset = startOffset, len = data.length; offset < len - 1; offset++) {
        if (_adts2.default.isHeader(data, offset)) {
          break;
        }
      }
      // if ADTS header does not start straight from the beginning of the PES payload, raise an error
      if (offset) {
        var reason, fatal;
        if (offset < len - 1) {
          reason = 'AAC PES did not start with ADTS header,offset:' + offset;
          fatal = false;
        } else {
          reason = 'no ADTS header found in AAC PES';
          fatal = true;
        }
        _logger.logger.warn('parsing error:' + reason);
        this.observer.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.FRAG_PARSING_ERROR, fatal: fatal, reason: reason });
        if (fatal) {
          return;
        }
      }

      _adts2.default.initTrackConfig(track, this.observer, data, offset, this.audioCodec);
      frameIndex = 0;
      frameDuration = _adts2.default.getFrameDuration(track.samplerate);

      // if last AAC frame is overflowing, we should ensure timestamps are contiguous:
      // first sample PTS should be equal to last sample PTS + frameDuration
      if (aacOverFlow && aacLastPTS) {
        var newPTS = aacLastPTS + frameDuration;
        if (Math.abs(newPTS - pts) > 1) {
          _logger.logger.log('AAC: align PTS for overlapping frames by ' + Math.round((newPTS - pts) / 90));
          pts = newPTS;
        }
      }

      //scan for aac samples
      while (offset < len) {
        if (_adts2.default.isHeader(data, offset) && offset + 5 < len) {
          var frame = _adts2.default.appendFrame(track, data, offset, pts, frameIndex);
          if (frame) {
            //logger.log(`${Math.round(frame.sample.pts)} : AAC`);
            offset += frame.length;
            stamp = frame.sample.pts;
            frameIndex++;
          } else {
            //logger.log('Unable to parse AAC frame');
            break;
          }
        } else {
          //nothing found, keep looking
          offset++;
        }
      }

      if (offset < len) {
        aacOverFlow = data.subarray(offset, len);
        //logger.log(`AAC: overflow detected:${len-offset}`);
      } else {
        aacOverFlow = null;
      }
      this.aacOverFlow = aacOverFlow;
      this.aacLastPTS = stamp;
    }
  }, {
    key: '_parseMPEGPES',
    value: function _parseMPEGPES(pes) {
      var data = pes.data;
      var length = data.length;
      var frameIndex = 0;
      var offset = 0;
      var pts = pes.pts;

      while (offset < length) {
        if (_mpegaudio2.default.isHeader(data, offset)) {
          var frame = _mpegaudio2.default.appendFrame(this._audioTrack, data, offset, pts, frameIndex);
          if (frame) {
            offset += frame.length;
            frameIndex++;
          } else {
            //logger.log('Unable to parse Mpeg audio frame');
            break;
          }
        } else {
          //nothing found, keep looking
          offset++;
        }
      }
    }
  }, {
    key: '_parseID3PES',
    value: function _parseID3PES(pes) {
      this._id3Track.samples.push(pes);
    }
  }], [{
    key: 'probe',
    value: function probe(data) {
      // a TS fragment should contain at least 3 TS packets, a PAT, a PMT, and one PID, each starting with 0x47
      if (data.length >= 3 * 188 && data[0] === 0x47 && data[188] === 0x47 && data[2 * 188] === 0x47) {
        return true;
      } else {
        return false;
      }
    }
  }]);

  return TSDemuxer;
}();

exports.default = TSDemuxer;

},{"22":22,"26":26,"30":30,"31":31,"33":33,"35":35,"54":54}],33:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
var ErrorTypes = exports.ErrorTypes = {
  // Identifier for a network error (loading error / timeout ...)
  NETWORK_ERROR: 'networkError',
  // Identifier for a media Error (video/parsing/mediasource error)
  MEDIA_ERROR: 'mediaError',
  // Identifier for a mux Error (demuxing/remuxing)
  MUX_ERROR: 'muxError',
  // Identifier for all other errors
  OTHER_ERROR: 'otherError'
};

var ErrorDetails = exports.ErrorDetails = {
  // Identifier for a manifest load error - data: { url : faulty URL, response : { code: error code, text: error text }}
  MANIFEST_LOAD_ERROR: 'manifestLoadError',
  // Identifier for a manifest load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}
  MANIFEST_LOAD_TIMEOUT: 'manifestLoadTimeOut',
  // Identifier for a manifest parsing error - data: { url : faulty URL, reason : error reason}
  MANIFEST_PARSING_ERROR: 'manifestParsingError',
  // Identifier for a manifest with only incompatible codecs error - data: { url : faulty URL, reason : error reason}
  MANIFEST_INCOMPATIBLE_CODECS_ERROR: 'manifestIncompatibleCodecsError',
  // Identifier for a level load error - data: { url : faulty URL, response : { code: error code, text: error text }}
  LEVEL_LOAD_ERROR: 'levelLoadError',
  // Identifier for a level load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}
  LEVEL_LOAD_TIMEOUT: 'levelLoadTimeOut',
  // Identifier for a level switch error - data: { level : faulty level Id, event : error description}
  LEVEL_SWITCH_ERROR: 'levelSwitchError',
  // Identifier for an audio track load error - data: { url : faulty URL, response : { code: error code, text: error text }}
  AUDIO_TRACK_LOAD_ERROR: 'audioTrackLoadError',
  // Identifier for an audio track load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}
  AUDIO_TRACK_LOAD_TIMEOUT: 'audioTrackLoadTimeOut',
  // Identifier for fragment load error - data: { frag : fragment object, response : { code: error code, text: error text }}
  FRAG_LOAD_ERROR: 'fragLoadError',
  // Identifier for fragment loop loading error - data: { frag : fragment object}
  FRAG_LOOP_LOADING_ERROR: 'fragLoopLoadingError',
  // Identifier for fragment load timeout error - data: { frag : fragment object}
  FRAG_LOAD_TIMEOUT: 'fragLoadTimeOut',
  // Identifier for a fragment decryption error event - data: {id : demuxer Id,frag: fragment object, reason : parsing error description }
  FRAG_DECRYPT_ERROR: 'fragDecryptError',
  // Identifier for a fragment parsing error event - data: { id : demuxer Id, reason : parsing error description }
  // will be renamed DEMUX_PARSING_ERROR and switched to MUX_ERROR in the next major release
  FRAG_PARSING_ERROR: 'fragParsingError',
  // Identifier for a remux alloc error event - data: { id : demuxer Id, frag : fragment object, bytes : nb of bytes on which allocation failed , reason : error text }
  REMUX_ALLOC_ERROR: 'remuxAllocError',
  // Identifier for decrypt key load error - data: { frag : fragment object, response : { code: error code, text: error text }}
  KEY_LOAD_ERROR: 'keyLoadError',
  // Identifier for decrypt key load timeout error - data: { frag : fragment object}
  KEY_LOAD_TIMEOUT: 'keyLoadTimeOut',
  // Triggered when an exception occurs while adding a sourceBuffer to MediaSource - data : {  err : exception , mimeType : mimeType }
  BUFFER_ADD_CODEC_ERROR: 'bufferAddCodecError',
  // Identifier for a buffer append error - data: append error description
  BUFFER_APPEND_ERROR: 'bufferAppendError',
  // Identifier for a buffer appending error event - data: appending error description
  BUFFER_APPENDING_ERROR: 'bufferAppendingError',
  // Identifier for a buffer stalled error event
  BUFFER_STALLED_ERROR: 'bufferStalledError',
  // Identifier for a buffer full event
  BUFFER_FULL_ERROR: 'bufferFullError',
  // Identifier for a buffer seek over hole event
  BUFFER_SEEK_OVER_HOLE: 'bufferSeekOverHole',
  // Identifier for a buffer nudge on stall (playback is stuck although currentTime is in a buffered area)
  BUFFER_NUDGE_ON_STALL: 'bufferNudgeOnStall',
  // Identifier for an internal exception happening inside hls.js while handling an event
  INTERNAL_EXCEPTION: 'internalException',
  // Malformed WebVTT contents
  WEBVTT_EXCEPTION: 'webVTTException'
};

},{}],34:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; };

var _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; }; }(); /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     *
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     * All objects in the event handling chain should inherit from this class
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     *
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     */

var _logger = _dereq_(54);

var _errors = _dereq_(33);

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var EventHandler = function () {
  function EventHandler(hls) {
    _classCallCheck(this, EventHandler);

    this.hls = hls;
    this.onEvent = this.onEvent.bind(this);

    for (var _len = arguments.length, events = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
      events[_key - 1] = arguments[_key];
    }

    this.handledEvents = events;
    this.useGenericHandler = true;

    this.registerListeners();
  }

  _createClass(EventHandler, [{
    key: 'destroy',
    value: function destroy() {
      this.unregisterListeners();
    }
  }, {
    key: 'isEventHandler',
    value: function isEventHandler() {
      return _typeof(this.handledEvents) === 'object' && this.handledEvents.length && typeof this.onEvent === 'function';
    }
  }, {
    key: 'registerListeners',
    value: function registerListeners() {
      if (this.isEventHandler()) {
        this.handledEvents.forEach(function (event) {
          if (event === 'hlsEventGeneric') {
            throw new Error('Forbidden event name: ' + event);
          }
          this.hls.on(event, this.onEvent);
        }, this);
      }
    }
  }, {
    key: 'unregisterListeners',
    value: function unregisterListeners() {
      if (this.isEventHandler()) {
        this.handledEvents.forEach(function (event) {
          this.hls.off(event, this.onEvent);
        }, this);
      }
    }

    /**
     * arguments: event (string), data (any)
     */

  }, {
    key: 'onEvent',
    value: function onEvent(event, data) {
      this.onEventGeneric(event, data);
    }
  }, {
    key: 'onEventGeneric',
    value: function onEventGeneric(event, data) {
      var eventToFunction = function eventToFunction(event, data) {
        var funcName = 'on' + event.replace('hls', '');
        if (typeof this[funcName] !== 'function') {
          throw new Error('Event ' + event + ' has no generic handler in this ' + this.constructor.name + ' class (tried ' + funcName + ')');
        }
        return this[funcName].bind(this, data);
      };
      try {
        eventToFunction.call(this, event, data).call();
      } catch (err) {
        _logger.logger.error('internal error happened while processing ' + event + ':' + err.message);
        this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.OTHER_ERROR, details: _errors.ErrorDetails.INTERNAL_EXCEPTION, fatal: false, event: event, err: err });
      }
    }
  }]);

  return EventHandler;
}();

exports.default = EventHandler;

},{"33":33,"35":35,"54":54}],35:[function(_dereq_,module,exports){
'use strict';

module.exports = {
  // fired before MediaSource is attaching to media element - data: { media }
  MEDIA_ATTACHING: 'hlsMediaAttaching',
  // fired when MediaSource has been succesfully attached to media element - data: { }
  MEDIA_ATTACHED: 'hlsMediaAttached',
  // fired before detaching MediaSource from media element - data: { }
  MEDIA_DETACHING: 'hlsMediaDetaching',
  // fired when MediaSource has been detached from media element - data: { }
  MEDIA_DETACHED: 'hlsMediaDetached',
  // fired when we buffer is going to be reset - data: { }
  BUFFER_RESET: 'hlsBufferReset',
  // fired when we know about the codecs that we need buffers for to push into - data: {tracks : { container, codec, levelCodec, initSegment, metadata }}
  BUFFER_CODECS: 'hlsBufferCodecs',
  // fired when sourcebuffers have been created - data: { tracks : tracks }
  BUFFER_CREATED: 'hlsBufferCreated',
  // fired when we append a segment to the buffer - data: { segment: segment object }
  BUFFER_APPENDING: 'hlsBufferAppending',
  // fired when we are done with appending a media segment to the buffer - data : { parent : segment parent that triggered BUFFER_APPENDING, pending : nb of segments waiting for appending for this segment parent}
  BUFFER_APPENDED: 'hlsBufferAppended',
  // fired when the stream is finished and we want to notify the media buffer that there will be no more data - data: { }
  BUFFER_EOS: 'hlsBufferEos',
  // fired when the media buffer should be flushed - data { startOffset, endOffset }
  BUFFER_FLUSHING: 'hlsBufferFlushing',
  // fired when the media buffer has been flushed - data: { }
  BUFFER_FLUSHED: 'hlsBufferFlushed',
  // fired to signal that a manifest loading starts - data: { url : manifestURL}
  MANIFEST_LOADING: 'hlsManifestLoading',
  // fired after manifest has been loaded - data: { levels : [available quality levels], audioTracks : [ available audio tracks], url : manifestURL, stats : { trequest, tfirst, tload, mtime}}
  MANIFEST_LOADED: 'hlsManifestLoaded',
  // fired after manifest has been parsed - data: { levels : [available quality levels], firstLevel : index of first quality level appearing in Manifest}
  MANIFEST_PARSED: 'hlsManifestParsed',
  // fired when a level switch is requested - data: { level : id of new level } // deprecated in favor LEVEL_SWITCHING
  LEVEL_SWITCH: 'hlsLevelSwitch',
  // fired when a level switch is requested - data: { level : id of new level }
  LEVEL_SWITCHING: 'hlsLevelSwitching',
  // fired when a level switch is effective - data: { level : id of new level }
  LEVEL_SWITCHED: 'hlsLevelSwitched',
  // fired when a level playlist loading starts - data: { url : level URL, level : id of level being loaded}
  LEVEL_LOADING: 'hlsLevelLoading',
  // fired when a level playlist loading finishes - data: { details : levelDetails object, level : id of loaded level, stats : { trequest, tfirst, tload, mtime} }
  LEVEL_LOADED: 'hlsLevelLoaded',
  // fired when a level's details have been updated based on previous details, after it has been loaded - data: { details : levelDetails object, level : id of updated level }
  LEVEL_UPDATED: 'hlsLevelUpdated',
  // fired when a level's PTS information has been updated after parsing a fragment - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment }
  LEVEL_PTS_UPDATED: 'hlsLevelPtsUpdated',
  // fired to notify that audio track lists has been updated - data: { audioTracks : audioTracks }
  AUDIO_TRACKS_UPDATED: 'hlsAudioTracksUpdated',
  // fired when an audio track switch occurs - data: { id : audio track id } // deprecated in favor AUDIO_TRACK_SWITCHING
  AUDIO_TRACK_SWITCH: 'hlsAudioTrackSwitch',
  // fired when an audio track switching is requested - data: { id : audio track id }
  AUDIO_TRACK_SWITCHING: 'hlsAudioTrackSwitching',
  // fired when an audio track switch actually occurs - data: { id : audio track id }
  AUDIO_TRACK_SWITCHED: 'hlsAudioTrackSwitched',
  // fired when an audio track loading starts - data: { url : audio track URL, id : audio track id }
  AUDIO_TRACK_LOADING: 'hlsAudioTrackLoading',
  // fired when an audio track loading finishes - data: { details : levelDetails object, id : audio track id, stats : { trequest, tfirst, tload, mtime } }
  AUDIO_TRACK_LOADED: 'hlsAudioTrackLoaded',
  // fired to notify that subtitle track lists has been updated - data: { subtitleTracks : subtitleTracks }
  SUBTITLE_TRACKS_UPDATED: 'hlsSubtitleTracksUpdated',
  // fired when an subtitle track switch occurs - data: { id : subtitle track id }
  SUBTITLE_TRACK_SWITCH: 'hlsSubtitleTrackSwitch',
  // fired when a subtitle track loading starts - data: { url : subtitle track URL, id : subtitle track id }
  SUBTITLE_TRACK_LOADING: 'hlsSubtitleTrackLoading',
  // fired when a subtitle track loading finishes - data: { details : levelDetails object, id : subtitle track id, stats : { trequest, tfirst, tload, mtime } }
  SUBTITLE_TRACK_LOADED: 'hlsSubtitleTrackLoaded',
  // fired when a subtitle fragment has been processed - data: { success : boolean, frag : the processed frag }
  SUBTITLE_FRAG_PROCESSED: 'hlsSubtitleFragProcessed',
  // fired when the first timestamp is found - data: { id : demuxer id, initPTS: initPTS, frag : fragment object }
  INIT_PTS_FOUND: 'hlsInitPtsFound',
  // fired when a fragment loading starts - data: { frag : fragment object }
  FRAG_LOADING: 'hlsFragLoading',
  // fired when a fragment loading is progressing - data: { frag : fragment object, { trequest, tfirst, loaded } }
  FRAG_LOAD_PROGRESS: 'hlsFragLoadProgress',
  // Identifier for fragment load aborting for emergency switch down - data: { frag : fragment object }
  FRAG_LOAD_EMERGENCY_ABORTED: 'hlsFragLoadEmergencyAborted',
  // fired when a fragment loading is completed - data: { frag : fragment object, payload : fragment payload, stats : { trequest, tfirst, tload, length } }
  FRAG_LOADED: 'hlsFragLoaded',
  // fired when a fragment has finished decrypting - data: { id : demuxer id, frag: fragment object, stats : { tstart, tdecrypt } }
  FRAG_DECRYPTED: 'hlsFragDecrypted',
  // fired when Init Segment has been extracted from fragment - data: { id : demuxer id, frag: fragment object, moov : moov MP4 box, codecs : codecs found while parsing fragment }
  FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment',
  // fired when parsing sei text is completed - data: { id : demuxer id, frag: fragment object, samples : [ sei samples pes ] }
  FRAG_PARSING_USERDATA: 'hlsFragParsingUserdata',
  // fired when parsing id3 is completed - data: { id : demuxer id, frag: fragment object, samples : [ id3 samples pes ] }
  FRAG_PARSING_METADATA: 'hlsFragParsingMetadata',
  // fired when data have been extracted from fragment - data: { id : demuxer id, frag: fragment object, data1 : moof MP4 box or TS fragments, data2 : mdat MP4 box or null}
  FRAG_PARSING_DATA: 'hlsFragParsingData',
  // fired when fragment parsing is completed - data: { id : demuxer id, frag: fragment object }
  FRAG_PARSED: 'hlsFragParsed',
  // fired when fragment remuxed MP4 boxes have all been appended into SourceBuffer - data: { id : demuxer id, frag : fragment object, stats : { trequest, tfirst, tload, tparsed, tbuffered, length, bwEstimate } }
  FRAG_BUFFERED: 'hlsFragBuffered',
  // fired when fragment matching with current media position is changing - data : { id : demuxer id, frag : fragment object }
  FRAG_CHANGED: 'hlsFragChanged',
  // Identifier for a FPS drop event - data: { curentDropped, currentDecoded, totalDroppedFrames }
  FPS_DROP: 'hlsFpsDrop',
  //triggered when FPS drop triggers auto level capping - data: { level, droppedlevel }
  FPS_DROP_LEVEL_CAPPING: 'hlsFpsDropLevelCapping',
  // Identifier for an error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data }
  ERROR: 'hlsError',
  // fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example - data: { }
  DESTROYING: 'hlsDestroying',
  // fired when a decrypt key loading starts - data: { frag : fragment object }
  KEY_LOADING: 'hlsKeyLoading',
  // fired when a decrypt key loading is completed - data: { frag : fragment object, payload : key payload, stats : { trequest, tfirst, tload, length } }
  KEY_LOADED: 'hlsKeyLoaded',
  // fired upon stream controller state transitions - data: { previousState, nextState }
  STREAM_STATE_TRANSITION: 'hlsStreamStateTransition'
};

},{}],36:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

/**
 *  AAC helper
 */

var AAC = function () {
  function AAC() {
    _classCallCheck(this, AAC);
  }

  _createClass(AAC, null, [{
    key: 'getSilentFrame',
    value: function getSilentFrame(codec, channelCount) {
      switch (codec) {
        case 'mp4a.40.2':
          if (channelCount === 1) {
            return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]);
          } else if (channelCount === 2) {
            return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]);
          } else if (channelCount === 3) {
            return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]);
          } else if (channelCount === 4) {
            return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]);
          } else if (channelCount === 5) {
            return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]);
          } else if (channelCount === 6) {
            return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x00, 0xb2, 0x00, 0x20, 0x08, 0xe0]);
          }
          break;
        // handle HE-AAC below (mp4a.40.5 / mp4a.40.29)
        default:
          if (channelCount === 1) {
            // ffmpeg -y -f lavfi -i "aevalsrc=0:d=0.05" -c:a libfdk_aac -profile:a aac_he -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac
            return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x4e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x1c, 0x6, 0xf1, 0xc1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]);
          } else if (channelCount === 2) {
            // ffmpeg -y -f lavfi -i "aevalsrc=0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac
            return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]);
          } else if (channelCount === 3) {
            // ffmpeg -y -f lavfi -i "aevalsrc=0|0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac
            return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]);
          }
          break;
      }
      return null;
    }
  }]);

  return AAC;
}();

exports.default = AAC;

},{}],37:[function(_dereq_,module,exports){
"use strict";

/**
 * Buffer Helper utils, providing methods dealing buffer length retrieval
*/

var BufferHelper = {
  isBuffered: function isBuffered(media, position) {
    if (media) {
      var buffered = media.buffered;
      for (var i = 0; i < buffered.length; i++) {
        if (position >= buffered.start(i) && position <= buffered.end(i)) {
          return true;
        }
      }
    }
    return false;
  },

  bufferInfo: function bufferInfo(media, pos, maxHoleDuration) {
    if (media) {
      var vbuffered = media.buffered,
          buffered = [],
          i;
      for (i = 0; i < vbuffered.length; i++) {
        buffered.push({ start: vbuffered.start(i), end: vbuffered.end(i) });
      }
      return this.bufferedInfo(buffered, pos, maxHoleDuration);
    } else {
      return { len: 0, start: pos, end: pos, nextStart: undefined };
    }
  },

  bufferedInfo: function bufferedInfo(buffered, pos, maxHoleDuration) {
    var buffered2 = [],

    // bufferStart and bufferEnd are buffer boundaries around current video position
    bufferLen,
        bufferStart,
        bufferEnd,
        bufferStartNext,
        i;
    // sort on buffer.start/smaller end (IE does not always return sorted buffered range)
    buffered.sort(function (a, b) {
      var diff = a.start - b.start;
      if (diff) {
        return diff;
      } else {
        return b.end - a.end;
      }
    });
    // there might be some small holes between buffer time range
    // consider that holes smaller than maxHoleDuration are irrelevant and build another
    // buffer time range representations that discards those holes
    for (i = 0; i < buffered.length; i++) {
      var buf2len = buffered2.length;
      if (buf2len) {
        var buf2end = buffered2[buf2len - 1].end;
        // if small hole (value between 0 or maxHoleDuration ) or overlapping (negative)
        if (buffered[i].start - buf2end < maxHoleDuration) {
          // merge overlapping time ranges
          // update lastRange.end only if smaller than item.end
          // e.g.  [ 1, 15] with  [ 2,8] => [ 1,15] (no need to modify lastRange.end)
          // whereas [ 1, 8] with  [ 2,15] => [ 1,15] ( lastRange should switch from [1,8] to [1,15])
          if (buffered[i].end > buf2end) {
            buffered2[buf2len - 1].end = buffered[i].end;
          }
        } else {
          // big hole
          buffered2.push(buffered[i]);
        }
      } else {
        // first value
        buffered2.push(buffered[i]);
      }
    }
    for (i = 0, bufferLen = 0, bufferStart = bufferEnd = pos; i < buffered2.length; i++) {
      var start = buffered2[i].start,
          end = buffered2[i].end;
      //logger.log('buf start/end:' + buffered.start(i) + '/' + buffered.end(i));
      if (pos + maxHoleDuration >= start && pos < end) {
        // play position is inside this buffer TimeRange, retrieve end of buffer position and buffer length
        bufferStart = start;
        bufferEnd = end;
        bufferLen = bufferEnd - pos;
      } else if (pos + maxHoleDuration < start) {
        bufferStartNext = start;
        break;
      }
    }
    return { len: bufferLen, start: bufferStart, end: bufferEnd, nextStart: bufferStartNext };
  }
};

module.exports = BufferHelper;

},{}],38:[function(_dereq_,module,exports){
'use strict';

var _logger = _dereq_(54);

var LevelHelper = {

  mergeDetails: function mergeDetails(oldDetails, newDetails) {
    var start = Math.max(oldDetails.startSN, newDetails.startSN) - newDetails.startSN,
        end = Math.min(oldDetails.endSN, newDetails.endSN) - newDetails.startSN,
        delta = newDetails.startSN - oldDetails.startSN,
        oldfragments = oldDetails.fragments,
        newfragments = newDetails.fragments,
        ccOffset = 0,
        PTSFrag;

    // check if old/new playlists have fragments in common
    if (end < start) {
      newDetails.PTSKnown = false;
      return;
    }
    // loop through overlapping SN and update startPTS , cc, and duration if any found
    for (var i = start; i <= end; i++) {
      var oldFrag = oldfragments[delta + i],
          newFrag = newfragments[i];
      if (newFrag && oldFrag) {
        ccOffset = oldFrag.cc - newFrag.cc;
        if (!isNaN(oldFrag.startPTS)) {
          newFrag.start = newFrag.startPTS = oldFrag.startPTS;
          newFrag.endPTS = oldFrag.endPTS;
          newFrag.duration = oldFrag.duration;
          newFrag.backtracked = oldFrag.backtracked;
          newFrag.dropped = oldFrag.dropped;
          PTSFrag = newFrag;
        }
      }
    }

    if (ccOffset) {
      _logger.logger.log('discontinuity sliding from playlist, take drift into account');
      for (i = 0; i < newfragments.length; i++) {
        newfragments[i].cc += ccOffset;
      }
    }

    // if at least one fragment contains PTS info, recompute PTS information for all fragments
    if (PTSFrag) {
      LevelHelper.updateFragPTSDTS(newDetails, PTSFrag, PTSFrag.startPTS, PTSFrag.endPTS, PTSFrag.startDTS, PTSFrag.endDTS);
    } else {
      // ensure that delta is within oldfragments range
      // also adjust sliding in case delta is 0 (we could have old=[50-60] and new=old=[50-61])
      // in that case we also need to adjust start offset of all fragments
      if (delta >= 0 && delta < oldfragments.length) {
        // adjust start by sliding offset
        var sliding = oldfragments[delta].start;
        for (i = 0; i < newfragments.length; i++) {
          newfragments[i].start += sliding;
        }
      }
    }
    // if we are here, it means we have fragments overlapping between
    // old and new level. reliable PTS info is thus relying on old level
    newDetails.PTSKnown = oldDetails.PTSKnown;
    return;
  },

  updateFragPTSDTS: function updateFragPTSDTS(details, frag, startPTS, endPTS, startDTS, endDTS) {
    // update frag PTS/DTS
    var maxStartPTS = startPTS;
    if (!isNaN(frag.startPTS)) {
      // delta PTS between audio and video
      var deltaPTS = Math.abs(frag.startPTS - startPTS);
      if (isNaN(frag.deltaPTS)) {
        frag.deltaPTS = deltaPTS;
      } else {
        frag.deltaPTS = Math.max(deltaPTS, frag.deltaPTS);
      }
      maxStartPTS = Math.max(startPTS, frag.startPTS);
      startPTS = Math.min(startPTS, frag.startPTS);
      endPTS = Math.max(endPTS, frag.endPTS);
      startDTS = Math.min(startDTS, frag.startDTS);
      endDTS = Math.max(endDTS, frag.endDTS);
    }

    var drift = startPTS - frag.start;
    frag.start = frag.startPTS = startPTS;
    frag.maxStartPTS = maxStartPTS;
    frag.endPTS = endPTS;
    frag.startDTS = startDTS;
    frag.endDTS = endDTS;
    frag.duration = endPTS - startPTS;

    var sn = frag.sn;
    // exit if sn out of range
    if (!details || sn < details.startSN || sn > details.endSN) {
      return 0;
    }
    var fragIdx, fragments, i;
    fragIdx = sn - details.startSN;
    fragments = details.fragments;
    // update frag reference in fragments array
    // rationale is that fragments array might not contain this frag object.
    // this will happpen if playlist has been refreshed between frag loading and call to updateFragPTSDTS()
    // if we don't update frag, we won't be able to propagate PTS info on the playlist
    // resulting in invalid sliding computation
    fragments[fragIdx] = frag;
    // adjust fragment PTS/duration from seqnum-1 to frag 0
    for (i = fragIdx; i > 0; i--) {
      LevelHelper.updatePTS(fragments, i, i - 1);
    }

    // adjust fragment PTS/duration from seqnum to last frag
    for (i = fragIdx; i < fragments.length - 1; i++) {
      LevelHelper.updatePTS(fragments, i, i + 1);
    }
    details.PTSKnown = true;
    //logger.log(`                                            frag start/end:${startPTS.toFixed(3)}/${endPTS.toFixed(3)}`);

    return drift;
  },

  updatePTS: function updatePTS(fragments, fromIdx, toIdx) {
    var fragFrom = fragments[fromIdx],
        fragTo = fragments[toIdx],
        fragToPTS = fragTo.startPTS;
    // if we know startPTS[toIdx]
    if (!isNaN(fragToPTS)) {
      // update fragment duration.
      // it helps to fix drifts between playlist reported duration and fragment real duration
      if (toIdx > fromIdx) {
        fragFrom.duration = fragToPTS - fragFrom.start;
        if (fragFrom.duration < 0) {
          _logger.logger.warn('negative duration computed for frag ' + fragFrom.sn + ',level ' + fragFrom.level + ', there should be some duration drift between playlist and fragment!');
        }
      } else {
        fragTo.duration = fragFrom.start - fragToPTS;
        if (fragTo.duration < 0) {
          _logger.logger.warn('negative duration computed for frag ' + fragTo.sn + ',level ' + fragTo.level + ', there should be some duration drift between playlist and fragment!');
        }
      }
    } else {
      // we dont know startPTS[toIdx]
      if (toIdx > fromIdx) {
        fragTo.start = fragFrom.start + fragFrom.duration;
      } else {
        fragTo.start = Math.max(fragFrom.start - fragTo.duration, 0);
      }
    }
  }
}; /**
    * Level Helper class, providing methods dealing with playlist sliding and drift
   */

module.exports = LevelHelper;

},{"54":54}],39:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }(); /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * HLS interface
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      */


var _urlToolkit = _dereq_(2);

var _urlToolkit2 = _interopRequireDefault(_urlToolkit);

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _errors = _dereq_(33);

var _playlistLoader = _dereq_(43);

var _playlistLoader2 = _interopRequireDefault(_playlistLoader);

var _fragmentLoader = _dereq_(41);

var _fragmentLoader2 = _interopRequireDefault(_fragmentLoader);

var _keyLoader = _dereq_(42);

var _keyLoader2 = _interopRequireDefault(_keyLoader);

var _streamController = _dereq_(13);

var _streamController2 = _interopRequireDefault(_streamController);

var _levelController = _dereq_(12);

var _levelController2 = _interopRequireDefault(_levelController);

var _id3TrackController = _dereq_(11);

var _id3TrackController2 = _interopRequireDefault(_id3TrackController);

var _logger = _dereq_(54);

var _events3 = _dereq_(1);

var _events4 = _interopRequireDefault(_events3);

var _config = _dereq_(4);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var Hls = function () {
  _createClass(Hls, null, [{
    key: 'isSupported',
    value: function isSupported() {
      var mediaSource = window.MediaSource = window.MediaSource || window.WebKitMediaSource;
      var sourceBuffer = window.SourceBuffer = window.SourceBuffer || window.WebKitSourceBuffer;
      var isTypeSupported = mediaSource && typeof mediaSource.isTypeSupported === 'function' && mediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"');

      // if SourceBuffer is exposed ensure its API is valid
      // safari and old version of Chrome doe not expose SourceBuffer globally so checking SourceBuffer.prototype is impossible
      var sourceBufferValidAPI = !sourceBuffer || sourceBuffer.prototype && typeof sourceBuffer.prototype.appendBuffer === 'function' && typeof sourceBuffer.prototype.remove === 'function';
      return isTypeSupported && sourceBufferValidAPI;
    }
  }, {
    key: 'version',
    get: function get() {
      // replaced with browserify-versionify transform
      return '0.7.11';
    }
  }, {
    key: 'Events',
    get: function get() {
      return _events2.default;
    }
  }, {
    key: 'ErrorTypes',
    get: function get() {
      return _errors.ErrorTypes;
    }
  }, {
    key: 'ErrorDetails',
    get: function get() {
      return _errors.ErrorDetails;
    }
  }, {
    key: 'DefaultConfig',
    get: function get() {
      if (!Hls.defaultConfig) {
        return _config.hlsDefaultConfig;
      }
      return Hls.defaultConfig;
    },
    set: function set(defaultConfig) {
      Hls.defaultConfig = defaultConfig;
    }
  }]);

  function Hls() {
    var _this = this;

    var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

    _classCallCheck(this, Hls);

    var defaultConfig = Hls.DefaultConfig;

    if ((config.liveSyncDurationCount || config.liveMaxLatencyDurationCount) && (config.liveSyncDuration || config.liveMaxLatencyDuration)) {
      throw new Error('Illegal hls.js config: don\'t mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration');
    }

    for (var prop in defaultConfig) {
      if (prop in config) {
        continue;
      }
      config[prop] = defaultConfig[prop];
    }

    if (config.liveMaxLatencyDurationCount !== undefined && config.liveMaxLatencyDurationCount <= config.liveSyncDurationCount) {
      throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be gt "liveSyncDurationCount"');
    }

    if (config.liveMaxLatencyDuration !== undefined && (config.liveMaxLatencyDuration <= config.liveSyncDuration || config.liveSyncDuration === undefined)) {
      throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be gt "liveSyncDuration"');
    }

    (0, _logger.enableLogs)(config.debug);
    this.config = config;
    this._autoLevelCapping = -1;
    // observer setup
    var observer = this.observer = new _events4.default();
    observer.trigger = function trigger(event) {
      for (var _len = arguments.length, data = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
        data[_key - 1] = arguments[_key];
      }

      observer.emit.apply(observer, [event, event].concat(data));
    };

    observer.off = function off(event) {
      for (var _len2 = arguments.length, data = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
        data[_key2 - 1] = arguments[_key2];
      }

      observer.removeListener.apply(observer, [event].concat(data));
    };
    this.on = observer.on.bind(observer);
    this.off = observer.off.bind(observer);
    this.trigger = observer.trigger.bind(observer);

    // core controllers and network loaders
    var abrController = this.abrController = new config.abrController(this);
    var bufferController = new config.bufferController(this);
    var capLevelController = new config.capLevelController(this);
    var fpsController = new config.fpsController(this);
    var playListLoader = new _playlistLoader2.default(this);
    var fragmentLoader = new _fragmentLoader2.default(this);
    var keyLoader = new _keyLoader2.default(this);
    var id3TrackController = new _id3TrackController2.default(this);

    // network controllers
    var levelController = this.levelController = new _levelController2.default(this);
    var streamController = this.streamController = new _streamController2.default(this);
    var networkControllers = [levelController, streamController];

    // optional audio stream controller
    var Controller = config.audioStreamController;
    if (Controller) {
      networkControllers.push(new Controller(this));
    }
    this.networkControllers = networkControllers;

    var coreComponents = [playListLoader, fragmentLoader, keyLoader, abrController, bufferController, capLevelController, fpsController, id3TrackController];

    // optional audio track and subtitle controller
    Controller = config.audioTrackController;
    if (Controller) {
      var audioTrackController = new Controller(this);
      this.audioTrackController = audioTrackController;
      coreComponents.push(audioTrackController);
    }

    Controller = config.subtitleTrackController;
    if (Controller) {
      var subtitleTrackController = new Controller(this);
      this.subtitleTrackController = subtitleTrackController;
      coreComponents.push(subtitleTrackController);
    }

    // optional subtitle controller
    [config.subtitleStreamController, config.timelineController].forEach(function (Controller) {
      if (Controller) {
        coreComponents.push(new Controller(_this));
      }
    });
    this.coreComponents = coreComponents;
  }

  _createClass(Hls, [{
    key: 'destroy',
    value: function destroy() {
      _logger.logger.log('destroy');
      this.trigger(_events2.default.DESTROYING);
      this.detachMedia();
      this.coreComponents.concat(this.networkControllers).forEach(function (component) {
        component.destroy();
      });
      this.url = null;
      this.observer.removeAllListeners();
      this._autoLevelCapping = -1;
    }
  }, {
    key: 'attachMedia',
    value: function attachMedia(media) {
      _logger.logger.log('attachMedia');
      this.media = media;
      this.trigger(_events2.default.MEDIA_ATTACHING, { media: media });
    }
  }, {
    key: 'detachMedia',
    value: function detachMedia() {
      _logger.logger.log('detachMedia');
      this.trigger(_events2.default.MEDIA_DETACHING);
      this.media = null;
    }
  }, {
    key: 'loadSource',
    value: function loadSource(url) {
      url = _urlToolkit2.default.buildAbsoluteURL(window.location.href, url, { alwaysNormalize: true });
      _logger.logger.log('loadSource:' + url);
      this.url = url;
      // when attaching to a source URL, trigger a playlist load
      this.trigger(_events2.default.MANIFEST_LOADING, { url: url });
    }
  }, {
    key: 'startLoad',
    value: function startLoad() {
      var startPosition = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : -1;

      _logger.logger.log('startLoad(' + startPosition + ')');
      this.networkControllers.forEach(function (controller) {
        controller.startLoad(startPosition);
      });
    }
  }, {
    key: 'stopLoad',
    value: function stopLoad() {
      _logger.logger.log('stopLoad');
      this.networkControllers.forEach(function (controller) {
        controller.stopLoad();
      });
    }
  }, {
    key: 'swapAudioCodec',
    value: function swapAudioCodec() {
      _logger.logger.log('swapAudioCodec');
      this.streamController.swapAudioCodec();
    }
  }, {
    key: 'recoverMediaError',
    value: function recoverMediaError() {
      _logger.logger.log('recoverMediaError');
      var media = this.media;
      this.detachMedia();
      this.attachMedia(media);
    }

    /** Return all quality levels **/

  }, {
    key: 'levels',
    get: function get() {
      return this.levelController.levels;
    }

    /** Return current playback quality level **/

  }, {
    key: 'currentLevel',
    get: function get() {
      return this.streamController.currentLevel;
    }

    /* set quality level immediately (-1 for automatic level selection) */
    ,
    set: function set(newLevel) {
      _logger.logger.log('set currentLevel:' + newLevel);
      this.loadLevel = newLevel;
      this.streamController.immediateLevelSwitch();
    }

    /** Return next playback quality level (quality level of next fragment) **/

  }, {
    key: 'nextLevel',
    get: function get() {
      return this.streamController.nextLevel;
    }

    /* set quality level for next fragment (-1 for automatic level selection) */
    ,
    set: function set(newLevel) {
      _logger.logger.log('set nextLevel:' + newLevel);
      this.levelController.manualLevel = newLevel;
      this.streamController.nextLevelSwitch();
    }

    /** Return the quality level of current/last loaded fragment **/

  }, {
    key: 'loadLevel',
    get: function get() {
      return this.levelController.level;
    }

    /* set quality level for current/next loaded fragment (-1 for automatic level selection) */
    ,
    set: function set(newLevel) {
      _logger.logger.log('set loadLevel:' + newLevel);
      this.levelController.manualLevel = newLevel;
    }

    /** Return the quality level of next loaded fragment **/

  }, {
    key: 'nextLoadLevel',
    get: function get() {
      return this.levelController.nextLoadLevel;
    }

    /** set quality level of next loaded fragment **/
    ,
    set: function set(level) {
      this.levelController.nextLoadLevel = level;
    }

    /** Return first level (index of first level referenced in manifest)
    **/

  }, {
    key: 'firstLevel',
    get: function get() {
      return Math.max(this.levelController.firstLevel, this.minAutoLevel);
    }

    /** set first level (index of first level referenced in manifest)
    **/
    ,
    set: function set(newLevel) {
      _logger.logger.log('set firstLevel:' + newLevel);
      this.levelController.firstLevel = newLevel;
    }

    /** Return start level (level of first fragment that will be played back)
        if not overrided by user, first level appearing in manifest will be used as start level
        if -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment)
    **/

  }, {
    key: 'startLevel',
    get: function get() {
      return this.levelController.startLevel;
    }

    /** set  start level (level of first fragment that will be played back)
        if not overrided by user, first level appearing in manifest will be used as start level
        if -1 : automatic start level selection, playback will start from level matching download bandwidth (determined from download of first segment)
    **/
    ,
    set: function set(newLevel) {
      _logger.logger.log('set startLevel:' + newLevel);
      var hls = this;
      // if not in automatic start level detection, ensure startLevel is greater than minAutoLevel
      if (newLevel !== -1) {
        newLevel = Math.max(newLevel, hls.minAutoLevel);
      }
      hls.levelController.startLevel = newLevel;
    }

    /** Return the capping/max level value that could be used by automatic level selection algorithm **/

  }, {
    key: 'autoLevelCapping',
    get: function get() {
      return this._autoLevelCapping;
    }

    /** set the capping/max level value that could be used by automatic level selection algorithm **/
    ,
    set: function set(newLevel) {
      _logger.logger.log('set autoLevelCapping:' + newLevel);
      this._autoLevelCapping = newLevel;
    }

    /* check if we are in automatic level selection mode */

  }, {
    key: 'autoLevelEnabled',
    get: function get() {
      return this.levelController.manualLevel === -1;
    }

    /* return manual level */

  }, {
    key: 'manualLevel',
    get: function get() {
      return this.levelController.manualLevel;
    }

    /* return min level selectable in auto mode according to config.minAutoBitrate */

  }, {
    key: 'minAutoLevel',
    get: function get() {
      var hls = this,
          levels = hls.levels,
          minAutoBitrate = hls.config.minAutoBitrate,
          len = levels ? levels.length : 0;
      for (var i = 0; i < len; i++) {
        var levelNextBitrate = levels[i].realBitrate ? Math.max(levels[i].realBitrate, levels[i].bitrate) : levels[i].bitrate;
        if (levelNextBitrate > minAutoBitrate) {
          return i;
        }
      }
      return 0;
    }

    /* return max level selectable in auto mode according to autoLevelCapping */

  }, {
    key: 'maxAutoLevel',
    get: function get() {
      var hls = this;
      var levels = hls.levels;
      var autoLevelCapping = hls.autoLevelCapping;
      var maxAutoLevel = void 0;
      if (autoLevelCapping === -1 && levels && levels.length) {
        maxAutoLevel = levels.length - 1;
      } else {
        maxAutoLevel = autoLevelCapping;
      }
      return maxAutoLevel;
    }

    // return next auto level

  }, {
    key: 'nextAutoLevel',
    get: function get() {
      var hls = this;
      // ensure next auto level is between  min and max auto level
      return Math.min(Math.max(hls.abrController.nextAutoLevel, hls.minAutoLevel), hls.maxAutoLevel);
    }

    // this setter is used to force next auto level
    // this is useful to force a switch down in auto mode : in case of load error on level N, hls.js can set nextAutoLevel to N-1 for example)
    // forced value is valid for one fragment. upon succesful frag loading at forced level, this value will be resetted to -1 by ABR controller
    ,
    set: function set(nextLevel) {
      var hls = this;
      hls.abrController.nextAutoLevel = Math.max(hls.minAutoLevel, nextLevel);
    }

    /** get alternate audio tracks list from playlist **/

  }, {
    key: 'audioTracks',
    get: function get() {
      var audioTrackController = this.audioTrackController;
      return audioTrackController ? audioTrackController.audioTracks : [];
    }

    /** get index of the selected audio track (index in audio track lists) **/

  }, {
    key: 'audioTrack',
    get: function get() {
      var audioTrackController = this.audioTrackController;
      return audioTrackController ? audioTrackController.audioTrack : -1;
    }

    /** select an audio track, based on its index in audio track lists**/
    ,
    set: function set(audioTrackId) {
      var audioTrackController = this.audioTrackController;
      if (audioTrackController) {
        audioTrackController.audioTrack = audioTrackId;
      }
    }
  }, {
    key: 'liveSyncPosition',
    get: function get() {
      return this.streamController.liveSyncPosition;
    }

    /** get alternate subtitle tracks list from playlist **/

  }, {
    key: 'subtitleTracks',
    get: function get() {
      var subtitleTrackController = this.subtitleTrackController;
      return subtitleTrackController ? subtitleTrackController.subtitleTracks : [];
    }

    /** get index of the selected subtitle track (index in subtitle track lists) **/

  }, {
    key: 'subtitleTrack',
    get: function get() {
      var subtitleTrackController = this.subtitleTrackController;
      return subtitleTrackController ? subtitleTrackController.subtitleTrack : -1;
    }

    /** select an subtitle track, based on its index in subtitle track lists**/
    ,
    set: function set(subtitleTrackId) {
      var subtitleTrackController = this.subtitleTrackController;
      if (subtitleTrackController) {
        subtitleTrackController.subtitleTrack = subtitleTrackId;
      }
    }
  }]);

  return Hls;
}();

exports.default = Hls;

},{"1":1,"11":11,"12":12,"13":13,"2":2,"33":33,"35":35,"4":4,"41":41,"42":42,"43":43,"54":54}],40:[function(_dereq_,module,exports){
'use strict';

// This is mostly for support of the es6 module export
// syntax with the babel compiler, it looks like it doesnt support
// function exports like we are used to in node/commonjs
module.exports = _dereq_(39).default;

},{"39":39}],41:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _errors = _dereq_(33);

var _logger = _dereq_(54);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * Fragment Loader
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               */

var FragmentLoader = function (_EventHandler) {
  _inherits(FragmentLoader, _EventHandler);

  function FragmentLoader(hls) {
    _classCallCheck(this, FragmentLoader);

    var _this = _possibleConstructorReturn(this, (FragmentLoader.__proto__ || Object.getPrototypeOf(FragmentLoader)).call(this, hls, _events2.default.FRAG_LOADING));

    _this.loaders = {};
    return _this;
  }

  _createClass(FragmentLoader, [{
    key: 'destroy',
    value: function destroy() {
      var loaders = this.loaders;
      for (var loaderName in loaders) {
        var loader = loaders[loaderName];
        if (loader) {
          loader.destroy();
        }
      }
      this.loaders = {};
      _eventHandler2.default.prototype.destroy.call(this);
    }
  }, {
    key: 'onFragLoading',
    value: function onFragLoading(data) {
      var frag = data.frag,
          type = frag.type,
          loader = this.loaders[type],
          config = this.hls.config;

      frag.loaded = 0;
      if (loader) {
        _logger.logger.warn('abort previous fragment loader for type:' + type);
        loader.abort();
      }
      loader = this.loaders[type] = frag.loader = typeof config.fLoader !== 'undefined' ? new config.fLoader(config) : new config.loader(config);

      var loaderContext = void 0,
          loaderConfig = void 0,
          loaderCallbacks = void 0;
      loaderContext = { url: frag.url, frag: frag, responseType: 'arraybuffer', progressData: false };
      var start = frag.byteRangeStartOffset,
          end = frag.byteRangeEndOffset;
      if (!isNaN(start) && !isNaN(end)) {
        loaderContext.rangeStart = start;
        loaderContext.rangeEnd = end;
      }
      loaderConfig = { timeout: config.fragLoadingTimeOut, maxRetry: 0, retryDelay: 0, maxRetryDelay: config.fragLoadingMaxRetryTimeout };
      loaderCallbacks = { onSuccess: this.loadsuccess.bind(this), onError: this.loaderror.bind(this), onTimeout: this.loadtimeout.bind(this), onProgress: this.loadprogress.bind(this) };
      loader.load(loaderContext, loaderConfig, loaderCallbacks);
    }
  }, {
    key: 'loadsuccess',
    value: function loadsuccess(response, stats, context) {
      var networkDetails = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;

      var payload = response.data,
          frag = context.frag;
      // detach fragment loader on load success
      frag.loader = undefined;
      this.loaders[frag.type] = undefined;
      this.hls.trigger(_events2.default.FRAG_LOADED, { payload: payload, frag: frag, stats: stats, networkDetails: networkDetails });
    }
  }, {
    key: 'loaderror',
    value: function loaderror(response, context) {
      var networkDetails = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;

      var loader = context.loader;
      if (loader) {
        loader.abort();
      }
      this.loaders[context.type] = undefined;
      this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.FRAG_LOAD_ERROR, fatal: false, frag: context.frag, response: response, networkDetails: networkDetails });
    }
  }, {
    key: 'loadtimeout',
    value: function loadtimeout(stats, context) {
      var networkDetails = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;

      var loader = context.loader;
      if (loader) {
        loader.abort();
      }
      this.loaders[context.type] = undefined;
      this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.FRAG_LOAD_TIMEOUT, fatal: false, frag: context.frag, networkDetails: networkDetails });
    }

    // data will be used for progressive parsing

  }, {
    key: 'loadprogress',
    value: function loadprogress(stats, context, data) {
      var networkDetails = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
      // jshint ignore:line
      var frag = context.frag;
      frag.loaded = stats.loaded;
      this.hls.trigger(_events2.default.FRAG_LOAD_PROGRESS, { frag: frag, stats: stats, networkDetails: networkDetails });
    }
  }]);

  return FragmentLoader;
}(_eventHandler2.default);

exports.default = FragmentLoader;

},{"33":33,"34":34,"35":35,"54":54}],42:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _errors = _dereq_(33);

var _logger = _dereq_(54);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _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; }

function _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; } /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                * Decrypt key Loader
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               */

var KeyLoader = function (_EventHandler) {
  _inherits(KeyLoader, _EventHandler);

  function KeyLoader(hls) {
    _classCallCheck(this, KeyLoader);

    var _this = _possibleConstructorReturn(this, (KeyLoader.__proto__ || Object.getPrototypeOf(KeyLoader)).call(this, hls, _events2.default.KEY_LOADING));

    _this.loaders = {};
    _this.decryptkey = null;
    _this.decrypturl = null;
    return _this;
  }

  _createClass(KeyLoader, [{
    key: 'destroy',
    value: function destroy() {
      for (var loaderName in this.loaders) {
        var loader = this.loaders[loaderName];
        if (loader) {
          loader.destroy();
        }
      }
      this.loaders = {};
      _eventHandler2.default.prototype.destroy.call(this);
    }
  }, {
    key: 'onKeyLoading',
    value: function onKeyLoading(data) {
      var frag = data.frag,
          type = frag.type,
          loader = this.loaders[type],
          decryptdata = frag.decryptdata,
          uri = decryptdata.uri;
      // if uri is different from previous one or if decrypt key not retrieved yet
      if (uri !== this.decrypturl || this.decryptkey === null) {
        var config = this.hls.config;

        if (loader) {
          _logger.logger.warn('abort previous key loader for type:' + type);
          loader.abort();
        }
        frag.loader = this.loaders[type] = new config.loader(config);
        this.decrypturl = uri;
        this.decryptkey = null;

        var loaderContext = void 0,
            loaderConfig = void 0,
            loaderCallbacks = void 0;
        loaderContext = { url: uri, frag: frag, responseType: 'arraybuffer' };
        loaderConfig = { timeout: config.fragLoadingTimeOut, maxRetry: config.fragLoadingMaxRetry, retryDelay: config.fragLoadingRetryDelay, maxRetryDelay: config.fragLoadingMaxRetryTimeout };
        loaderCallbacks = { onSuccess: this.loadsuccess.bind(this), onError: this.loaderror.bind(this), onTimeout: this.loadtimeout.bind(this) };
        frag.loader.load(loaderContext, loaderConfig, loaderCallbacks);
      } else if (this.decryptkey) {
        // we already loaded this key, return it
        decryptdata.key = this.decryptkey;
        this.hls.trigger(_events2.default.KEY_LOADED, { frag: frag });
      }
    }
  }, {
    key: 'loadsuccess',
    value: function loadsuccess(response, stats, context) {
      var frag = context.frag;
      this.decryptkey = frag.decryptdata.key = new Uint8Array(response.data);
      // detach fragment loader on load success
      frag.loader = undefined;
      this.loaders[frag.type] = undefined;
      this.hls.trigger(_events2.default.KEY_LOADED, { frag: frag });
    }
  }, {
    key: 'loaderror',
    value: function loaderror(response, context) {
      var frag = context.frag,
          loader = frag.loader;
      if (loader) {
        loader.abort();
      }
      this.loaders[context.type] = undefined;
      this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.KEY_LOAD_ERROR, fatal: false, frag: frag, response: response });
    }
  }, {
    key: 'loadtimeout',
    value: function loadtimeout(stats, context) {
      var frag = context.frag,
          loader = frag.loader;
      if (loader) {
        loader.abort();
      }
      this.loaders[context.type] = undefined;
      this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.KEY_LOAD_TIMEOUT, fatal: false, frag: frag });
    }
  }]);

  return KeyLoader;
}(_eventHandler2.default);

exports.default = KeyLoader;

},{"33":33,"34":34,"35":35,"54":54}],43:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }(); /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * Playlist Loader
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     */

var _urlToolkit = _dereq_(2);

var _urlToolkit2 = _interopRequireDefault(_urlToolkit);

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _eventHandler = _dereq_(34);

var _eventHandler2 = _interopRequireDefault(_eventHandler);

var _errors = _dereq_(33);

var _attrList = _dereq_(47);

var _attrList2 = _interopRequireDefault(_attrList);

var _logger = _dereq_(54);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _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; }

function _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; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

// https://regex101.com is your friend
var MASTER_PLAYLIST_REGEX = /#EXT-X-STREAM-INF:([^\n\r]*)[\r\n]+([^\r\n]+)/g;
var MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g;

var LEVEL_PLAYLIST_REGEX_FAST = new RegExp([/#EXTINF:(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source, // duration (#EXTINF:<duration>,<title>), group 1 => duration, group 2 => title
/|(?!#)(\S+)/.source, // segment URI, group 3 => the URI (note newline is not eaten)
/|#EXT-X-BYTERANGE:*(.+)/.source, // next segment's byterange, group 4 => range spec (x@y)
/|#EXT-X-PROGRAM-DATE-TIME:(.+)/.source, // next segment's program date/time group 5 => the datetime spec
/|#.*/.source // All other non-segment oriented tags will match with all groups empty
].join(''), 'g');

var LEVEL_PLAYLIST_REGEX_SLOW = /(?:(?:#(EXTM3U))|(?:#EXT-X-(PLAYLIST-TYPE):(.+))|(?:#EXT-X-(MEDIA-SEQUENCE): *(\d+))|(?:#EXT-X-(TARGETDURATION): *(\d+))|(?:#EXT-X-(KEY):(.+))|(?:#EXT-X-(START):(.+))|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DISCONTINUITY-SEQ)UENCE:(\d+))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(VERSION):(\d+))|(?:#EXT-X-(MAP):(.+))|(?:(#)(.*):(.*))|(?:(#)(.*))(?:.*)\r?\n?/;

var LevelKey = function () {
  function LevelKey() {
    _classCallCheck(this, LevelKey);

    this.method = null;
    this.key = null;
    this.iv = null;
    this._uri = null;
  }

  _createClass(LevelKey, [{
    key: 'uri',
    get: function get() {
      if (!this._uri && this.reluri) {
        this._uri = _urlToolkit2.default.buildAbsoluteURL(this.baseuri, this.reluri, { alwaysNormalize: true });
      }
      return this._uri;
    }
  }]);

  return LevelKey;
}();

var Fragment = function () {
  function Fragment() {
    _classCallCheck(this, Fragment);

    this._url = null;
    this._byteRange = null;
    this._decryptdata = null;
    this.tagList = [];
  }

  _createClass(Fragment, [{
    key: 'createInitializationVector',


    /**
     * Utility method for parseLevelPlaylist to create an initialization vector for a given segment
     * @returns {Uint8Array}
     */
    value: function createInitializationVector(segmentNumber) {
      var uint8View = new Uint8Array(16);

      for (var i = 12; i < 16; i++) {
        uint8View[i] = segmentNumber >> 8 * (15 - i) & 0xff;
      }

      return uint8View;
    }

    /**
     * Utility method for parseLevelPlaylist to get a fragment's decryption data from the currently parsed encryption key data
     * @param levelkey - a playlist's encryption info
     * @param segmentNumber - the fragment's segment number
     * @returns {*} - an object to be applied as a fragment's decryptdata
     */

  }, {
    key: 'fragmentDecryptdataFromLevelkey',
    value: function fragmentDecryptdataFromLevelkey(levelkey, segmentNumber) {
      var decryptdata = levelkey;

      if (levelkey && levelkey.method && levelkey.uri && !levelkey.iv) {
        decryptdata = new LevelKey();
        decryptdata.method = levelkey.method;
        decryptdata.baseuri = levelkey.baseuri;
        decryptdata.reluri = levelkey.reluri;
        decryptdata.iv = this.createInitializationVector(segmentNumber);
      }

      return decryptdata;
    }
  }, {
    key: 'cloneObj',
    value: function cloneObj(obj) {
      return JSON.parse(JSON.stringify(obj));
    }
  }, {
    key: 'url',
    get: function get() {
      if (!this._url && this.relurl) {
        this._url = _urlToolkit2.default.buildAbsoluteURL(this.baseurl, this.relurl, { alwaysNormalize: true });
      }
      return this._url;
    },
    set: function set(value) {
      this._url = value;
    }
  }, {
    key: 'programDateTime',
    get: function get() {
      if (!this._programDateTime && this.rawProgramDateTime) {
        this._programDateTime = new Date(Date.parse(this.rawProgramDateTime));
      }
      return this._programDateTime;
    }
  }, {
    key: 'byteRange',
    get: function get() {
      if (!this._byteRange) {
        var byteRange = this._byteRange = [];
        if (this.rawByteRange) {
          var params = this.rawByteRange.split('@', 2);
          if (params.length === 1) {
            var lastByteRangeEndOffset = this.lastByteRangeEndOffset;
            byteRange[0] = lastByteRangeEndOffset ? lastByteRangeEndOffset : 0;
          } else {
            byteRange[0] = parseInt(params[1]);
          }
          byteRange[1] = parseInt(params[0]) + byteRange[0];
        }
      }
      return this._byteRange;
    }
  }, {
    key: 'byteRangeStartOffset',
    get: function get() {
      return this.byteRange[0];
    }
  }, {
    key: 'byteRangeEndOffset',
    get: function get() {
      return this.byteRange[1];
    }
  }, {
    key: 'decryptdata',
    get: function get() {
      if (!this._decryptdata) {
        this._decryptdata = this.fragmentDecryptdataFromLevelkey(this.levelkey, this.sn);
      }
      return this._decryptdata;
    }
  }]);

  return Fragment;
}();

var PlaylistLoader = function (_EventHandler) {
  _inherits(PlaylistLoader, _EventHandler);

  function PlaylistLoader(hls) {
    _classCallCheck(this, PlaylistLoader);

    var _this = _possibleConstructorReturn(this, (PlaylistLoader.__proto__ || Object.getPrototypeOf(PlaylistLoader)).call(this, hls, _events2.default.MANIFEST_LOADING, _events2.default.LEVEL_LOADING, _events2.default.AUDIO_TRACK_LOADING, _events2.default.SUBTITLE_TRACK_LOADING));

    _this.loaders = {};
    return _this;
  }

  _createClass(PlaylistLoader, [{
    key: 'destroy',
    value: function destroy() {
      for (var loaderName in this.loaders) {
        var loader = this.loaders[loaderName];
        if (loader) {
          loader.destroy();
        }
      }
      this.loaders = {};
      _eventHandler2.default.prototype.destroy.call(this);
    }
  }, {
    key: 'onManifestLoading',
    value: function onManifestLoading(data) {
      this.load(data.url, { type: 'manifest' });
    }
  }, {
    key: 'onLevelLoading',
    value: function onLevelLoading(data) {
      this.load(data.url, { type: 'level', level: data.level, id: data.id });
    }
  }, {
    key: 'onAudioTrackLoading',
    value: function onAudioTrackLoading(data) {
      this.load(data.url, { type: 'audioTrack', id: data.id });
    }
  }, {
    key: 'onSubtitleTrackLoading',
    value: function onSubtitleTrackLoading(data) {
      this.load(data.url, { type: 'subtitleTrack', id: data.id });
    }
  }, {
    key: 'load',
    value: function load(url, context) {
      var loader = this.loaders[context.type];
      if (loader) {
        var loaderContext = loader.context;
        if (loaderContext && loaderContext.url === url) {
          _logger.logger.trace('playlist request ongoing');
          return;
        } else {
          _logger.logger.warn('abort previous loader for type:' + context.type);
          loader.abort();
        }
      }
      var config = this.hls.config,
          retry = void 0,
          timeout = void 0,
          retryDelay = void 0,
          maxRetryDelay = void 0;
      if (context.type === 'manifest') {
        retry = config.manifestLoadingMaxRetry;
        timeout = config.manifestLoadingTimeOut;
        retryDelay = config.manifestLoadingRetryDelay;
        maxRetryDelay = config.manifestLoadingMaxRetryTimeout;
      } else {
        retry = config.levelLoadingMaxRetry;
        timeout = config.levelLoadingTimeOut;
        retryDelay = config.levelLoadingRetryDelay;
        maxRetryDelay = config.levelLoadingMaxRetryTimeout;
        _logger.logger.log('loading playlist for ' + context.type + ' ' + (context.level || context.id));
      }
      loader = this.loaders[context.type] = context.loader = typeof config.pLoader !== 'undefined' ? new config.pLoader(config) : new config.loader(config);
      context.url = url;
      context.responseType = '';

      var loaderConfig = void 0,
          loaderCallbacks = void 0;
      loaderConfig = { timeout: timeout, maxRetry: retry, retryDelay: retryDelay, maxRetryDelay: maxRetryDelay };
      loaderCallbacks = { onSuccess: this.loadsuccess.bind(this), onError: this.loaderror.bind(this), onTimeout: this.loadtimeout.bind(this) };
      loader.load(context, loaderConfig, loaderCallbacks);
    }
  }, {
    key: 'resolve',
    value: function resolve(url, baseUrl) {
      return _urlToolkit2.default.buildAbsoluteURL(baseUrl, url, { alwaysNormalize: true });
    }
  }, {
    key: 'parseMasterPlaylist',
    value: function parseMasterPlaylist(string, baseurl) {
      var levels = [],
          result = void 0;
      MASTER_PLAYLIST_REGEX.lastIndex = 0;
      while ((result = MASTER_PLAYLIST_REGEX.exec(string)) != null) {
        var level = {};

        var attrs = level.attrs = new _attrList2.default(result[1]);
        level.url = this.resolve(result[2], baseurl);

        var resolution = attrs.decimalResolution('RESOLUTION');
        if (resolution) {
          level.width = resolution.width;
          level.height = resolution.height;
        }
        level.bitrate = attrs.decimalInteger('AVERAGE-BANDWIDTH') || attrs.decimalInteger('BANDWIDTH');
        level.name = attrs.NAME;

        var codecs = attrs.CODECS;
        if (codecs) {
          codecs = codecs.split(/[ ,]+/);
          for (var i = 0; i < codecs.length; i++) {
            var codec = codecs[i];
            if (codec.indexOf('avc1') !== -1) {
              level.videoCodec = this.avc1toavcoti(codec);
            } else if (codec.indexOf('hvc1') !== -1) {
              level.videoCodec = codec;
            } else {
              level.audioCodec = codec;
            }
          }
        }

        levels.push(level);
      }
      return levels;
    }
  }, {
    key: 'parseMasterPlaylistMedia',
    value: function parseMasterPlaylistMedia(string, baseurl, type) {
      var audioCodec = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;

      var result = void 0,
          medias = [],
          id = 0;
      MASTER_PLAYLIST_MEDIA_REGEX.lastIndex = 0;
      while ((result = MASTER_PLAYLIST_MEDIA_REGEX.exec(string)) != null) {
        var media = {};
        var attrs = new _attrList2.default(result[1]);
        if (attrs.TYPE === type) {
          media.groupId = attrs['GROUP-ID'];
          media.name = attrs.NAME;
          media.type = type;
          media.default = attrs.DEFAULT === 'YES';
          media.autoselect = attrs.AUTOSELECT === 'YES';
          media.forced = attrs.FORCED === 'YES';
          if (attrs.URI) {
            media.url = this.resolve(attrs.URI, baseurl);
          }
          media.lang = attrs.LANGUAGE;
          if (!media.name) {
            media.name = media.lang;
          }
          if (audioCodec) {
            media.audioCodec = audioCodec;
          }
          media.id = id++;
          medias.push(media);
        }
      }
      return medias;
    }
  }, {
    key: 'avc1toavcoti',
    value: function avc1toavcoti(codec) {
      var result,
          avcdata = codec.split('.');
      if (avcdata.length > 2) {
        result = avcdata.shift() + '.';
        result += parseInt(avcdata.shift()).toString(16);
        result += ('000' + parseInt(avcdata.shift()).toString(16)).substr(-4);
      } else {
        result = codec;
      }
      return result;
    }
  }, {
    key: 'parseLevelPlaylist',
    value: function parseLevelPlaylist(string, baseurl, id, type) {
      var currentSN = 0,
          totalduration = 0,
          level = { type: null, version: null, url: baseurl, fragments: [], live: true, startSN: 0 },
          levelkey = new LevelKey(),
          cc = 0,
          prevFrag = null,
          frag = new Fragment(),
          result,
          i;

      LEVEL_PLAYLIST_REGEX_FAST.lastIndex = 0;

      while ((result = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) !== null) {
        var duration = result[1];
        if (duration) {
          // INF
          frag.duration = parseFloat(duration);
          // avoid sliced strings    https://github.com/video-dev/hls.js/issues/939
          var title = (' ' + result[2]).slice(1);
          frag.title = title ? title : null;
          frag.tagList.push(title ? ['INF', duration, title] : ['INF', duration]);
        } else if (result[3]) {
          // url
          if (!isNaN(frag.duration)) {
            var sn = currentSN++;
            frag.type = type;
            frag.start = totalduration;
            frag.levelkey = levelkey;
            frag.sn = sn;
            frag.level = id;
            frag.cc = cc;
            frag.baseurl = baseurl;
            // avoid sliced strings    https://github.com/video-dev/hls.js/issues/939
            frag.relurl = (' ' + result[3]).slice(1);

            level.fragments.push(frag);
            prevFrag = frag;
            totalduration += frag.duration;

            frag = new Fragment();
          }
        } else if (result[4]) {
          // X-BYTERANGE
          frag.rawByteRange = (' ' + result[4]).slice(1);
          if (prevFrag) {
            var lastByteRangeEndOffset = prevFrag.byteRangeEndOffset;
            if (lastByteRangeEndOffset) {
              frag.lastByteRangeEndOffset = lastByteRangeEndOffset;
            }
          }
        } else if (result[5]) {
          // PROGRAM-DATE-TIME
          // avoid sliced strings    https://github.com/video-dev/hls.js/issues/939
          frag.rawProgramDateTime = (' ' + result[5]).slice(1);
          frag.tagList.push(['PROGRAM-DATE-TIME', frag.rawProgramDateTime]);
        } else {
          result = result[0].match(LEVEL_PLAYLIST_REGEX_SLOW);
          for (i = 1; i < result.length; i++) {
            if (result[i] !== undefined) {
              break;
            }
          }

          // avoid sliced strings    https://github.com/video-dev/hls.js/issues/939
          var value1 = (' ' + result[i + 1]).slice(1);
          var value2 = (' ' + result[i + 2]).slice(1);

          switch (result[i]) {
            case '#':
              frag.tagList.push(value2 ? [value1, value2] : [value1]);
              break;
            case 'PLAYLIST-TYPE':
              level.type = value1.toUpperCase();
              break;
            case 'MEDIA-SEQUENCE':
              currentSN = level.startSN = parseInt(value1);
              break;
            case 'TARGETDURATION':
              level.targetduration = parseFloat(value1);
              break;
            case 'VERSION':
              level.version = parseInt(value1);
              break;
            case 'EXTM3U':
              break;
            case 'ENDLIST':
              level.live = false;
              break;
            case 'DIS':
              cc++;
              frag.tagList.push(['DIS']);
              break;
            case 'DISCONTINUITY-SEQ':
              cc = parseInt(value1);
              break;
            case 'KEY':
              // https://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.4.4
              var decryptparams = value1;
              var keyAttrs = new _attrList2.default(decryptparams);
              var decryptmethod = keyAttrs.enumeratedString('METHOD'),
                  decrypturi = keyAttrs.URI,
                  decryptiv = keyAttrs.hexadecimalInteger('IV');
              if (decryptmethod) {
                levelkey = new LevelKey();
                if (decrypturi && ['AES-128', 'SAMPLE-AES'].indexOf(decryptmethod) >= 0) {
                  levelkey.method = decryptmethod;
                  // URI to get the key
                  levelkey.baseuri = baseurl;
                  levelkey.reluri = decrypturi;
                  levelkey.key = null;
                  // Initialization Vector (IV)
                  levelkey.iv = decryptiv;
                }
              }
              break;
            case 'START':
              var startParams = value1;
              var startAttrs = new _attrList2.default(startParams);
              var startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET');
              //TIME-OFFSET can be 0
              if (!isNaN(startTimeOffset)) {
                level.startTimeOffset = startTimeOffset;
              }
              break;
            case 'MAP':
              var mapAttrs = new _attrList2.default(value1);
              frag.relurl = mapAttrs.URI;
              frag.rawByteRange = mapAttrs.BYTERANGE;
              frag.baseurl = baseurl;
              frag.level = id;
              frag.type = type;
              frag.sn = 'initSegment';
              level.initSegment = frag;
              frag = new Fragment();
              break;
            default:
              _logger.logger.warn('line parsed but not handled: ' + result);
              break;
          }
        }
      }
      frag = prevFrag;
      //logger.log('found ' + level.fragments.length + ' fragments');
      if (frag && !frag.relurl) {
        level.fragments.pop();
        totalduration -= frag.duration;
      }
      level.totalduration = totalduration;
      level.averagetargetduration = totalduration / level.fragments.length;
      level.endSN = currentSN - 1;
      return level;
    }
  }, {
    key: 'loadsuccess',
    value: function loadsuccess(response, stats, context) {
      var networkDetails = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;

      var string = response.data,
          url = response.url,
          type = context.type,
          id = context.id,
          level = context.level,
          hls = this.hls;

      this.loaders[type] = undefined;
      // responseURL not supported on some browsers (it is used to detect URL redirection)
      // data-uri mode also not supported (but no need to detect redirection)
      if (url === undefined || url.indexOf('data:') === 0) {
        // fallback to initial URL
        url = context.url;
      }
      stats.tload = performance.now();
      //stats.mtime = new Date(target.getResponseHeader('Last-Modified'));
      if (string.indexOf('#EXTM3U') === 0) {
        if (string.indexOf('#EXTINF:') > 0) {
          var isLevel = type !== 'audioTrack' && type !== 'subtitleTrack',
              levelId = !isNaN(level) ? level : !isNaN(id) ? id : 0,
              levelDetails = this.parseLevelPlaylist(string, url, levelId, type === 'audioTrack' ? 'audio' : type === 'subtitleTrack' ? 'subtitle' : 'main');
          levelDetails.tload = stats.tload;
          if (type === 'manifest') {
            // first request, stream manifest (no master playlist), fire manifest loaded event with level details
            hls.trigger(_events2.default.MANIFEST_LOADED, { levels: [{ url: url, details: levelDetails }], audioTracks: [], url: url, stats: stats, networkDetails: networkDetails });
          }
          stats.tparsed = performance.now();
          if (levelDetails.targetduration) {
            if (isLevel) {
              hls.trigger(_events2.default.LEVEL_LOADED, { details: levelDetails, level: level || 0, id: id || 0, stats: stats, networkDetails: networkDetails });
            } else {
              if (type === 'audioTrack') {
                hls.trigger(_events2.default.AUDIO_TRACK_LOADED, { details: levelDetails, id: id, stats: stats, networkDetails: networkDetails });
              } else if (type === 'subtitleTrack') {
                hls.trigger(_events2.default.SUBTITLE_TRACK_LOADED, { details: levelDetails, id: id, stats: stats, networkDetails: networkDetails });
              }
            }
          } else {
            hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.MANIFEST_PARSING_ERROR, fatal: true, url: url, reason: 'invalid targetduration', networkDetails: networkDetails });
          }
        } else {
          var levels = this.parseMasterPlaylist(string, url);
          // multi level playlist, parse level info
          if (levels.length) {
            var audioTracks = this.parseMasterPlaylistMedia(string, url, 'AUDIO', levels[0].audioCodec);
            var subtitles = this.parseMasterPlaylistMedia(string, url, 'SUBTITLES');
            if (audioTracks.length) {
              // check if we have found an audio track embedded in main playlist (audio track without URI attribute)
              var embeddedAudioFound = false;
              audioTracks.forEach(function (audioTrack) {
                if (!audioTrack.url) {
                  embeddedAudioFound = true;
                }
              });
              // if no embedded audio track defined, but audio codec signaled in quality level, we need to signal this main audio track
              // this could happen with playlists with alt audio rendition in which quality levels (main) contains both audio+video. but with mixed audio track not signaled
              if (embeddedAudioFound === false && levels[0].audioCodec && !levels[0].attrs.AUDIO) {
                _logger.logger.log('audio codec signaled in quality level, but no embedded audio track signaled, create one');
                audioTracks.unshift({ type: 'main', name: 'main' });
              }
            }
            hls.trigger(_events2.default.MANIFEST_LOADED, { levels: levels, audioTracks: audioTracks, subtitles: subtitles, url: url, stats: stats, networkDetails: networkDetails });
          } else {
            hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.MANIFEST_PARSING_ERROR, fatal: true, url: url, reason: 'no level found in manifest', networkDetails: networkDetails });
          }
        }
      } else {
        hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: _errors.ErrorDetails.MANIFEST_PARSING_ERROR, fatal: true, url: url, reason: 'no EXTM3U delimiter', networkDetails: networkDetails });
      }
    }
  }, {
    key: 'loaderror',
    value: function loaderror(response, context) {
      var networkDetails = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;

      var details,
          fatal,
          loader = context.loader;
      switch (context.type) {
        case 'manifest':
          details = _errors.ErrorDetails.MANIFEST_LOAD_ERROR;
          fatal = true;
          break;
        case 'level':
          details = _errors.ErrorDetails.LEVEL_LOAD_ERROR;
          fatal = false;
          break;
        case 'audioTrack':
          details = _errors.ErrorDetails.AUDIO_TRACK_LOAD_ERROR;
          fatal = false;
          break;
      }
      if (loader) {
        loader.abort();
        this.loaders[context.type] = undefined;
      }
      this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: details, fatal: fatal, url: loader.url, loader: loader, response: response, context: context, networkDetails: networkDetails });
    }
  }, {
    key: 'loadtimeout',
    value: function loadtimeout(stats, context) {
      var networkDetails = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;

      var details,
          fatal,
          loader = context.loader;
      switch (context.type) {
        case 'manifest':
          details = _errors.ErrorDetails.MANIFEST_LOAD_TIMEOUT;
          fatal = true;
          break;
        case 'level':
          details = _errors.ErrorDetails.LEVEL_LOAD_TIMEOUT;
          fatal = false;
          break;
        case 'audioTrack':
          details = _errors.ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT;
          fatal = false;
          break;
      }
      if (loader) {
        loader.abort();
        this.loaders[context.type] = undefined;
      }
      this.hls.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.NETWORK_ERROR, details: details, fatal: fatal, url: loader.url, loader: loader, context: context, networkDetails: networkDetails });
    }
  }]);

  return PlaylistLoader;
}(_eventHandler2.default);

exports.default = PlaylistLoader;

},{"2":2,"33":33,"34":34,"35":35,"47":47,"54":54}],44:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

/**
 * Generate MP4 Box
*/

//import Hex from '../utils/hex';

var UINT32_MAX = Math.pow(2, 32) - 1;

var MP4 = function () {
  function MP4() {
    _classCallCheck(this, MP4);
  }

  _createClass(MP4, null, [{
    key: 'init',
    value: function init() {
      MP4.types = {
        avc1: [], // codingname
        avcC: [],
        btrt: [],
        dinf: [],
        dref: [],
        esds: [],
        ftyp: [],
        hdlr: [],
        mdat: [],
        mdhd: [],
        mdia: [],
        mfhd: [],
        minf: [],
        moof: [],
        moov: [],
        mp4a: [],
        '.mp3': [],
        mvex: [],
        mvhd: [],
        pasp: [],
        sdtp: [],
        stbl: [],
        stco: [],
        stsc: [],
        stsd: [],
        stsz: [],
        stts: [],
        tfdt: [],
        tfhd: [],
        traf: [],
        trak: [],
        trun: [],
        trex: [],
        tkhd: [],
        vmhd: [],
        smhd: []
      };

      var i;
      for (i in MP4.types) {
        if (MP4.types.hasOwnProperty(i)) {
          MP4.types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)];
        }
      }

      var videoHdlr = new Uint8Array([0x00, // version 0
      0x00, 0x00, 0x00, // flags
      0x00, 0x00, 0x00, 0x00, // pre_defined
      0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
      0x00, 0x00, 0x00, 0x00, // reserved
      0x00, 0x00, 0x00, 0x00, // reserved
      0x00, 0x00, 0x00, 0x00, // reserved
      0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler'
      ]);

      var audioHdlr = new Uint8Array([0x00, // version 0
      0x00, 0x00, 0x00, // flags
      0x00, 0x00, 0x00, 0x00, // pre_defined
      0x73, 0x6f, 0x75, 0x6e, // handler_type: 'soun'
      0x00, 0x00, 0x00, 0x00, // reserved
      0x00, 0x00, 0x00, 0x00, // reserved
      0x00, 0x00, 0x00, 0x00, // reserved
      0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler'
      ]);

      MP4.HDLR_TYPES = {
        'video': videoHdlr,
        'audio': audioHdlr
      };

      var dref = new Uint8Array([0x00, // version 0
      0x00, 0x00, 0x00, // flags
      0x00, 0x00, 0x00, 0x01, // entry_count
      0x00, 0x00, 0x00, 0x0c, // entry_size
      0x75, 0x72, 0x6c, 0x20, // 'url' type
      0x00, // version 0
      0x00, 0x00, 0x01 // entry_flags
      ]);

      var stco = new Uint8Array([0x00, // version
      0x00, 0x00, 0x00, // flags
      0x00, 0x00, 0x00, 0x00 // entry_count
      ]);

      MP4.STTS = MP4.STSC = MP4.STCO = stco;

      MP4.STSZ = new Uint8Array([0x00, // version
      0x00, 0x00, 0x00, // flags
      0x00, 0x00, 0x00, 0x00, // sample_size
      0x00, 0x00, 0x00, 0x00]);
      MP4.VMHD = new Uint8Array([0x00, // version
      0x00, 0x00, 0x01, // flags
      0x00, 0x00, // graphicsmode
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // opcolor
      ]);
      MP4.SMHD = new Uint8Array([0x00, // version
      0x00, 0x00, 0x00, // flags
      0x00, 0x00, // balance
      0x00, 0x00 // reserved
      ]);

      MP4.STSD = new Uint8Array([0x00, // version 0
      0x00, 0x00, 0x00, // flags
      0x00, 0x00, 0x00, 0x01]); // entry_count

      var majorBrand = new Uint8Array([105, 115, 111, 109]); // isom
      var avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1
      var minorVersion = new Uint8Array([0, 0, 0, 1]);

      MP4.FTYP = MP4.box(MP4.types.ftyp, majorBrand, minorVersion, majorBrand, avc1Brand);
      MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref));
    }
  }, {
    key: 'box',
    value: function box(type) {
      var payload = Array.prototype.slice.call(arguments, 1),
          size = 8,
          i = payload.length,
          len = i,
          result;
      // calculate the total size we need to allocate
      while (i--) {
        size += payload[i].byteLength;
      }
      result = new Uint8Array(size);
      result[0] = size >> 24 & 0xff;
      result[1] = size >> 16 & 0xff;
      result[2] = size >> 8 & 0xff;
      result[3] = size & 0xff;
      result.set(type, 4);
      // copy the payload into the result
      for (i = 0, size = 8; i < len; i++) {
        // copy payload[i] array @ offset size
        result.set(payload[i], size);
        size += payload[i].byteLength;
      }
      return result;
    }
  }, {
    key: 'hdlr',
    value: function hdlr(type) {
      return MP4.box(MP4.types.hdlr, MP4.HDLR_TYPES[type]);
    }
  }, {
    key: 'mdat',
    value: function mdat(data) {
      return MP4.box(MP4.types.mdat, data);
    }
  }, {
    key: 'mdhd',
    value: function mdhd(timescale, duration) {
      duration *= timescale;
      var upperWordDuration = Math.floor(duration / (UINT32_MAX + 1));
      var lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1));
      return MP4.box(MP4.types.mdhd, new Uint8Array([0x01, // version 1
      0x00, 0x00, 0x00, // flags
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // creation_time
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // modification_time
      timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF, // timescale
      upperWordDuration >> 24, upperWordDuration >> 16 & 0xFF, upperWordDuration >> 8 & 0xFF, upperWordDuration & 0xFF, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xFF, lowerWordDuration >> 8 & 0xFF, lowerWordDuration & 0xFF, 0x55, 0xc4, // 'und' language (undetermined)
      0x00, 0x00]));
    }
  }, {
    key: 'mdia',
    value: function mdia(track) {
      return MP4.box(MP4.types.mdia, MP4.mdhd(track.timescale, track.duration), MP4.hdlr(track.type), MP4.minf(track));
    }
  }, {
    key: 'mfhd',
    value: function mfhd(sequenceNumber) {
      return MP4.box(MP4.types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // flags
      sequenceNumber >> 24, sequenceNumber >> 16 & 0xFF, sequenceNumber >> 8 & 0xFF, sequenceNumber & 0xFF]) // sequence_number
      );
    }
  }, {
    key: 'minf',
    value: function minf(track) {
      if (track.type === 'audio') {
        return MP4.box(MP4.types.minf, MP4.box(MP4.types.smhd, MP4.SMHD), MP4.DINF, MP4.stbl(track));
      } else {
        return MP4.box(MP4.types.minf, MP4.box(MP4.types.vmhd, MP4.VMHD), MP4.DINF, MP4.stbl(track));
      }
    }
  }, {
    key: 'moof',
    value: function moof(sn, baseMediaDecodeTime, track) {
      return MP4.box(MP4.types.moof, MP4.mfhd(sn), MP4.traf(track, baseMediaDecodeTime));
    }
    /**
     * @param tracks... (optional) {array} the tracks associated with this movie
     */

  }, {
    key: 'moov',
    value: function moov(tracks) {
      var i = tracks.length,
          boxes = [];

      while (i--) {
        boxes[i] = MP4.trak(tracks[i]);
      }

      return MP4.box.apply(null, [MP4.types.moov, MP4.mvhd(tracks[0].timescale, tracks[0].duration)].concat(boxes).concat(MP4.mvex(tracks)));
    }
  }, {
    key: 'mvex',
    value: function mvex(tracks) {
      var i = tracks.length,
          boxes = [];

      while (i--) {
        boxes[i] = MP4.trex(tracks[i]);
      }
      return MP4.box.apply(null, [MP4.types.mvex].concat(boxes));
    }
  }, {
    key: 'mvhd',
    value: function mvhd(timescale, duration) {
      duration *= timescale;
      var upperWordDuration = Math.floor(duration / (UINT32_MAX + 1));
      var lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1));
      var bytes = new Uint8Array([0x01, // version 1
      0x00, 0x00, 0x00, // flags
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // creation_time
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // modification_time
      timescale >> 24 & 0xFF, timescale >> 16 & 0xFF, timescale >> 8 & 0xFF, timescale & 0xFF, // timescale
      upperWordDuration >> 24, upperWordDuration >> 16 & 0xFF, upperWordDuration >> 8 & 0xFF, upperWordDuration & 0xFF, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xFF, lowerWordDuration >> 8 & 0xFF, lowerWordDuration & 0xFF, 0x00, 0x01, 0x00, 0x00, // 1.0 rate
      0x01, 0x00, // 1.0 volume
      0x00, 0x00, // reserved
      0x00, 0x00, 0x00, 0x00, // reserved
      0x00, 0x00, 0x00, 0x00, // reserved
      0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined
      0xff, 0xff, 0xff, 0xff // next_track_ID
      ]);
      return MP4.box(MP4.types.mvhd, bytes);
    }
  }, {
    key: 'sdtp',
    value: function sdtp(track) {
      var samples = track.samples || [],
          bytes = new Uint8Array(4 + samples.length),
          flags,
          i;
      // leave the full box header (4 bytes) all zero
      // write the sample table
      for (i = 0; i < samples.length; i++) {
        flags = samples[i].flags;
        bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy;
      }

      return MP4.box(MP4.types.sdtp, bytes);
    }
  }, {
    key: 'stbl',
    value: function stbl(track) {
      return MP4.box(MP4.types.stbl, MP4.stsd(track), MP4.box(MP4.types.stts, MP4.STTS), MP4.box(MP4.types.stsc, MP4.STSC), MP4.box(MP4.types.stsz, MP4.STSZ), MP4.box(MP4.types.stco, MP4.STCO));
    }
  }, {
    key: 'avc1',
    value: function avc1(track) {
      var sps = [],
          pps = [],
          i,
          data,
          len;
      // assemble the SPSs

      for (i = 0; i < track.sps.length; i++) {
        data = track.sps[i];
        len = data.byteLength;
        sps.push(len >>> 8 & 0xFF);
        sps.push(len & 0xFF);
        sps = sps.concat(Array.prototype.slice.call(data)); // SPS
      }

      // assemble the PPSs
      for (i = 0; i < track.pps.length; i++) {
        data = track.pps[i];
        len = data.byteLength;
        pps.push(len >>> 8 & 0xFF);
        pps.push(len & 0xFF);
        pps = pps.concat(Array.prototype.slice.call(data));
      }

      var avcc = MP4.box(MP4.types.avcC, new Uint8Array([0x01, // version
      sps[3], // profile
      sps[4], // profile compat
      sps[5], // level
      0xfc | 3, // lengthSizeMinusOne, hard-coded to 4 bytes
      0xE0 | track.sps.length // 3bit reserved (111) + numOfSequenceParameterSets
      ].concat(sps).concat([track.pps.length // numOfPictureParameterSets
      ]).concat(pps))),
          // "PPS"
      width = track.width,
          height = track.height,
          hSpacing = track.pixelRatio[0],
          vSpacing = track.pixelRatio[1];
      //console.log('avcc:' + Hex.hexDump(avcc));
      return MP4.box(MP4.types.avc1, new Uint8Array([0x00, 0x00, 0x00, // reserved
      0x00, 0x00, 0x00, // reserved
      0x00, 0x01, // data_reference_index
      0x00, 0x00, // pre_defined
      0x00, 0x00, // reserved
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // pre_defined
      width >> 8 & 0xFF, width & 0xff, // width
      height >> 8 & 0xFF, height & 0xff, // height
      0x00, 0x48, 0x00, 0x00, // horizresolution
      0x00, 0x48, 0x00, 0x00, // vertresolution
      0x00, 0x00, 0x00, 0x00, // reserved
      0x00, 0x01, // frame_count
      0x12, 0x64, 0x61, 0x69, 0x6C, //dailymotion/hls.js
      0x79, 0x6D, 0x6F, 0x74, 0x69, 0x6F, 0x6E, 0x2F, 0x68, 0x6C, 0x73, 0x2E, 0x6A, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // compressorname
      0x00, 0x18, // depth = 24
      0x11, 0x11]), // pre_defined = -1
      avcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
      0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
      0x00, 0x2d, 0xc6, 0xc0])), // avgBitrate
      MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24, // hSpacing
      hSpacing >> 16 & 0xFF, hSpacing >> 8 & 0xFF, hSpacing & 0xFF, vSpacing >> 24, // vSpacing
      vSpacing >> 16 & 0xFF, vSpacing >> 8 & 0xFF, vSpacing & 0xFF])));
    }
  }, {
    key: 'esds',
    value: function esds(track) {
      var configlen = track.config.length;
      return new Uint8Array([0x00, // version 0
      0x00, 0x00, 0x00, // flags

      0x03, // descriptor_type
      0x17 + configlen, // length
      0x00, 0x01, //es_id
      0x00, // stream_priority

      0x04, // descriptor_type
      0x0f + configlen, // length
      0x40, //codec : mpeg4_audio
      0x15, // stream_type
      0x00, 0x00, 0x00, // buffer_size
      0x00, 0x00, 0x00, 0x00, // maxBitrate
      0x00, 0x00, 0x00, 0x00, // avgBitrate

      0x05 // descriptor_type
      ].concat([configlen]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor
    }
  }, {
    key: 'mp4a',
    value: function mp4a(track) {
      var samplerate = track.samplerate;
      return MP4.box(MP4.types.mp4a, new Uint8Array([0x00, 0x00, 0x00, // reserved
      0x00, 0x00, 0x00, // reserved
      0x00, 0x01, // data_reference_index
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
      0x00, track.channelCount, // channelcount
      0x00, 0x10, // sampleSize:16bits
      0x00, 0x00, 0x00, 0x00, // reserved2
      samplerate >> 8 & 0xFF, samplerate & 0xff, //
      0x00, 0x00]), MP4.box(MP4.types.esds, MP4.esds(track)));
    }
  }, {
    key: 'mp3',
    value: function mp3(track) {
      var samplerate = track.samplerate;
      return MP4.box(MP4.types['.mp3'], new Uint8Array([0x00, 0x00, 0x00, // reserved
      0x00, 0x00, 0x00, // reserved
      0x00, 0x01, // data_reference_index
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
      0x00, track.channelCount, // channelcount
      0x00, 0x10, // sampleSize:16bits
      0x00, 0x00, 0x00, 0x00, // reserved2
      samplerate >> 8 & 0xFF, samplerate & 0xff, //
      0x00, 0x00]));
    }
  }, {
    key: 'stsd',
    value: function stsd(track) {
      if (track.type === 'audio') {
        if (!track.isAAC && track.codec === 'mp3') {
          return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp3(track));
        }
        return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
      } else {
        return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
      }
    }
  }, {
    key: 'tkhd',
    value: function tkhd(track) {
      var id = track.id,
          duration = track.duration * track.timescale,
          width = track.width,
          height = track.height,
          upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)),
          lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1));
      return MP4.box(MP4.types.tkhd, new Uint8Array([0x01, // version 1
      0x00, 0x00, 0x07, // flags
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // creation_time
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // modification_time
      id >> 24 & 0xFF, id >> 16 & 0xFF, id >> 8 & 0xFF, id & 0xFF, // track_ID
      0x00, 0x00, 0x00, 0x00, // reserved
      upperWordDuration >> 24, upperWordDuration >> 16 & 0xFF, upperWordDuration >> 8 & 0xFF, upperWordDuration & 0xFF, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xFF, lowerWordDuration >> 8 & 0xFF, lowerWordDuration & 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // reserved
      0x00, 0x00, // layer
      0x00, 0x00, // alternate_group
      0x00, 0x00, // non-audio track volume
      0x00, 0x00, // reserved
      0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
      width >> 8 & 0xFF, width & 0xFF, 0x00, 0x00, // width
      height >> 8 & 0xFF, height & 0xFF, 0x00, 0x00 // height
      ]));
    }
  }, {
    key: 'traf',
    value: function traf(track, baseMediaDecodeTime) {
      var sampleDependencyTable = MP4.sdtp(track),
          id = track.id,
          upperWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1)),
          lowerWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1));
      return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([0x00, // version 0
      0x00, 0x00, 0x00, // flags
      id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF]) // track_ID
      ), MP4.box(MP4.types.tfdt, new Uint8Array([0x01, // version 1
      0x00, 0x00, 0x00, // flags
      upperWordBaseMediaDecodeTime >> 24, upperWordBaseMediaDecodeTime >> 16 & 0XFF, upperWordBaseMediaDecodeTime >> 8 & 0XFF, upperWordBaseMediaDecodeTime & 0xFF, lowerWordBaseMediaDecodeTime >> 24, lowerWordBaseMediaDecodeTime >> 16 & 0XFF, lowerWordBaseMediaDecodeTime >> 8 & 0XFF, lowerWordBaseMediaDecodeTime & 0xFF])), MP4.trun(track, sampleDependencyTable.length + 16 + // tfhd
      20 + // tfdt
      8 + // traf header
      16 + // mfhd
      8 + // moof header
      8), // mdat header
      sampleDependencyTable);
    }

    /**
     * Generate a track box.
     * @param track {object} a track definition
     * @return {Uint8Array} the track box
     */

  }, {
    key: 'trak',
    value: function trak(track) {
      track.duration = track.duration || 0xffffffff;
      return MP4.box(MP4.types.trak, MP4.tkhd(track), MP4.mdia(track));
    }
  }, {
    key: 'trex',
    value: function trex(track) {
      var id = track.id;
      return MP4.box(MP4.types.trex, new Uint8Array([0x00, // version 0
      0x00, 0x00, 0x00, // flags
      id >> 24, id >> 16 & 0XFF, id >> 8 & 0XFF, id & 0xFF, // track_ID
      0x00, 0x00, 0x00, 0x01, // default_sample_description_index
      0x00, 0x00, 0x00, 0x00, // default_sample_duration
      0x00, 0x00, 0x00, 0x00, // default_sample_size
      0x00, 0x01, 0x00, 0x01 // default_sample_flags
      ]));
    }
  }, {
    key: 'trun',
    value: function trun(track, offset) {
      var samples = track.samples || [],
          len = samples.length,
          arraylen = 12 + 16 * len,
          array = new Uint8Array(arraylen),
          i,
          sample,
          duration,
          size,
          flags,
          cts;
      offset += 8 + arraylen;
      array.set([0x00, // version 0
      0x00, 0x0f, 0x01, // flags
      len >>> 24 & 0xFF, len >>> 16 & 0xFF, len >>> 8 & 0xFF, len & 0xFF, // sample_count
      offset >>> 24 & 0xFF, offset >>> 16 & 0xFF, offset >>> 8 & 0xFF, offset & 0xFF // data_offset
      ], 0);
      for (i = 0; i < len; i++) {
        sample = samples[i];
        duration = sample.duration;
        size = sample.size;
        flags = sample.flags;
        cts = sample.cts;
        array.set([duration >>> 24 & 0xFF, duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, // sample_duration
        size >>> 24 & 0xFF, size >>> 16 & 0xFF, size >>> 8 & 0xFF, size & 0xFF, // sample_size
        flags.isLeading << 2 | flags.dependsOn, flags.isDependedOn << 6 | flags.hasRedundancy << 4 | flags.paddingValue << 1 | flags.isNonSync, flags.degradPrio & 0xF0 << 8, flags.degradPrio & 0x0F, // sample_flags
        cts >>> 24 & 0xFF, cts >>> 16 & 0xFF, cts >>> 8 & 0xFF, cts & 0xFF // sample_composition_time_offset
        ], 12 + 16 * i);
      }
      return MP4.box(MP4.types.trun, array);
    }
  }, {
    key: 'initSegment',
    value: function initSegment(tracks) {
      if (!MP4.types) {
        MP4.init();
      }
      var movie = MP4.moov(tracks),
          result;
      result = new Uint8Array(MP4.FTYP.byteLength + movie.byteLength);
      result.set(MP4.FTYP);
      result.set(movie, MP4.FTYP.byteLength);
      return result;
    }
  }]);

  return MP4;
}();

exports.default = MP4;

},{}],45:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }(); /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     * fMP4 remuxer
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     */

var _aac = _dereq_(36);

var _aac2 = _interopRequireDefault(_aac);

var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

var _logger = _dereq_(54);

var _mp4Generator = _dereq_(44);

var _mp4Generator2 = _interopRequireDefault(_mp4Generator);

var _errors = _dereq_(33);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

// 10 seconds
var MAX_SILENT_FRAME_DURATION = 10 * 1000;

var MP4Remuxer = function () {
  function MP4Remuxer(observer, config, typeSupported, vendor) {
    _classCallCheck(this, MP4Remuxer);

    this.observer = observer;
    this.config = config;
    this.typeSupported = typeSupported;
    var userAgent = navigator.userAgent;
    this.isSafari = vendor && vendor.indexOf('Apple') > -1 && userAgent && !userAgent.match('CriOS');
    this.ISGenerated = false;
  }

  _createClass(MP4Remuxer, [{
    key: 'destroy',
    value: function destroy() {}
  }, {
    key: 'resetTimeStamp',
    value: function resetTimeStamp(defaultTimeStamp) {
      this._initPTS = this._initDTS = defaultTimeStamp;
    }
  }, {
    key: 'resetInitSegment',
    value: function resetInitSegment() {
      this.ISGenerated = false;
    }
  }, {
    key: 'remux',
    value: function remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) {
      // generate Init Segment if needed
      if (!this.ISGenerated) {
        this.generateIS(audioTrack, videoTrack, timeOffset);
      } else {
        if (accurateTimeOffset) {
          // check timestamp consistency. it there is more than 10s gap between expected PTS/DTS, recompute initPTS/DTS
          var refPTS = this._initPTS;
          var ptsNormalize = this._PTSNormalize;
          var timeScale = audioTrack.inputTimeScale || videoTrack.inputTimeScale;
          var initPTS = Infinity,
              initDTS = Infinity;
          var samples = audioTrack.samples;
          if (samples.length) {
            initPTS = initDTS = ptsNormalize(samples[0].pts - timeScale * timeOffset, refPTS);
          }
          samples = videoTrack.samples;
          if (samples.length) {
            var sample = samples[0];
            initPTS = Math.min(initPTS, ptsNormalize(sample.pts - timeScale * timeOffset, refPTS));
            initDTS = Math.min(initDTS, ptsNormalize(sample.dts - timeScale * timeOffset, refPTS));
          }
          if (initPTS !== Infinity) {
            var initPTSDelta = refPTS - initPTS;
            if (Math.abs(initPTSDelta) > 10 * timeScale) {
              _logger.logger.warn('timestamp inconsistency, ' + (initPTSDelta / timeScale).toFixed(3) + 's delta against expected value: missing discontinuity ? reset initPTS/initDTS');
              this._initPTS = initPTS;
              this._initDTS = initDTS;
              this.observer.trigger(_events2.default.INIT_PTS_FOUND, { initPTS: initPTS });
            }
          }
        }
      }

      if (this.ISGenerated) {
        // Purposefully remuxing audio before video, so that remuxVideo can use nextAudioPts, which is
        // calculated in remuxAudio.
        //logger.log('nb AAC samples:' + audioTrack.samples.length);
        if (audioTrack.samples.length) {
          // if initSegment was generated without video samples, regenerate it again
          if (!audioTrack.timescale) {
            _logger.logger.warn('regenerate InitSegment as audio detected');
            this.generateIS(audioTrack, videoTrack, timeOffset);
          }
          var audioData = this.remuxAudio(audioTrack, timeOffset, contiguous, accurateTimeOffset);
          //logger.log('nb AVC samples:' + videoTrack.samples.length);
          if (videoTrack.samples.length) {
            var audioTrackLength = void 0;
            if (audioData) {
              audioTrackLength = audioData.endPTS - audioData.startPTS;
            }
            // if initSegment was generated without video samples, regenerate it again
            if (!videoTrack.timescale) {
              _logger.logger.warn('regenerate InitSegment as video detected');
              this.generateIS(audioTrack, videoTrack, timeOffset);
            }
            this.remuxVideo(videoTrack, timeOffset, contiguous, audioTrackLength, accurateTimeOffset);
          }
        } else {
          var videoData = void 0;
          //logger.log('nb AVC samples:' + videoTrack.samples.length);
          if (videoTrack.samples.length) {
            videoData = this.remuxVideo(videoTrack, timeOffset, contiguous, accurateTimeOffset);
          }
          if (videoData && audioTrack.codec) {
            this.remuxEmptyAudio(audioTrack, timeOffset, contiguous, videoData);
          }
        }
      }
      //logger.log('nb ID3 samples:' + audioTrack.samples.length);
      if (id3Track.samples.length) {
        this.remuxID3(id3Track, timeOffset);
      }
      //logger.log('nb ID3 samples:' + audioTrack.samples.length);
      if (textTrack.samples.length) {
        this.remuxText(textTrack, timeOffset);
      }
      //notify end of parsing
      this.observer.trigger(_events2.default.FRAG_PARSED);
    }
  }, {
    key: 'generateIS',
    value: function generateIS(audioTrack, videoTrack, timeOffset) {
      var observer = this.observer,
          audioSamples = audioTrack.samples,
          videoSamples = videoTrack.samples,
          typeSupported = this.typeSupported,
          container = 'audio/mp4',
          tracks = {},
          data = { tracks: tracks },
          computePTSDTS = this._initPTS === undefined,
          initPTS,
          initDTS;

      if (computePTSDTS) {
        initPTS = initDTS = Infinity;
      }
      if (audioTrack.config && audioSamples.length) {
        // let's use audio sampling rate as MP4 time scale.
        // rationale is that there is a integer nb of audio frames per audio sample (1024 for AAC)
        // using audio sampling rate here helps having an integer MP4 frame duration
        // this avoids potential rounding issue and AV sync issue
        audioTrack.timescale = audioTrack.samplerate;
        _logger.logger.log('audio sampling rate : ' + audioTrack.samplerate);
        if (!audioTrack.isAAC) {
          if (typeSupported.mpeg) {
            // Chrome and Safari
            container = 'audio/mpeg';
            audioTrack.codec = '';
          } else if (typeSupported.mp3) {
            // Firefox
            audioTrack.codec = 'mp3';
          }
        }
        tracks.audio = {
          container: container,
          codec: audioTrack.codec,
          initSegment: !audioTrack.isAAC && typeSupported.mpeg ? new Uint8Array() : _mp4Generator2.default.initSegment([audioTrack]),
          metadata: {
            channelCount: audioTrack.channelCount
          }
        };
        if (computePTSDTS) {
          // remember first PTS of this demuxing context. for audio, PTS = DTS
          initPTS = initDTS = audioSamples[0].pts - audioTrack.inputTimeScale * timeOffset;
        }
      }

      if (videoTrack.sps && videoTrack.pps && videoSamples.length) {
        // let's use input time scale as MP4 video timescale
        // we use input time scale straight away to avoid rounding issues on frame duration / cts computation
        var inputTimeScale = videoTrack.inputTimeScale;
        videoTrack.timescale = inputTimeScale;
        tracks.video = {
          container: 'video/mp4',
          codec: videoTrack.codec,
          initSegment: _mp4Generator2.default.initSegment([videoTrack]),
          metadata: {
            width: videoTrack.width,
            height: videoTrack.height
          }
        };
        if (computePTSDTS) {
          initPTS = Math.min(initPTS, videoSamples[0].pts - inputTimeScale * timeOffset);
          initDTS = Math.min(initDTS, videoSamples[0].dts - inputTimeScale * timeOffset);
          this.observer.trigger(_events2.default.INIT_PTS_FOUND, { initPTS: initPTS });
        }
      }

      if (Object.keys(tracks).length) {
        observer.trigger(_events2.default.FRAG_PARSING_INIT_SEGMENT, data);
        this.ISGenerated = true;
        if (computePTSDTS) {
          this._initPTS = initPTS;
          this._initDTS = initDTS;
        }
      } else {
        observer.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MEDIA_ERROR, details: _errors.ErrorDetails.FRAG_PARSING_ERROR, fatal: false, reason: 'no audio/video samples found' });
      }
    }
  }, {
    key: 'remuxVideo',
    value: function remuxVideo(track, timeOffset, contiguous, audioTrackLength, accurateTimeOffset) {
      var offset = 8,
          timeScale = track.timescale,
          mp4SampleDuration,
          mdat,
          moof,
          firstPTS,
          firstDTS,
          nextDTS,
          lastPTS,
          lastDTS,
          inputSamples = track.samples,
          outputSamples = [],
          nbSamples = inputSamples.length,
          ptsNormalize = this._PTSNormalize,
          initDTS = this._initDTS;

      // for (let i = 0; i < track.samples.length; i++) {
      //   let avcSample = track.samples[i];
      //   let units = avcSample.units;
      //   let unitsString = '';
      //   for (let j = 0; j < units.length ; j++) {
      //     unitsString += units[j].type + ',';
      //     if (units[j].data.length < 500) {
      //       unitsString += Hex.hexDump(units[j].data);
      //     }
      //   }
      //   logger.log(avcSample.pts + '/' + avcSample.dts + ',' + unitsString + avcSample.units.length);
      // }

      // if parsed fragment is contiguous with last one, let's use last DTS value as reference
      var nextAvcDts = this.nextAvcDts;

      var isSafari = this.isSafari;

      // Safari does not like overlapping DTS on consecutive fragments. let's use nextAvcDts to overcome this if fragments are consecutive
      if (isSafari) {
        // also consider consecutive fragments as being contiguous (even if a level switch occurs),
        // for sake of clarity:
        // consecutive fragments are frags with
        //  - less than 100ms gaps between new time offset (if accurate) and next expected PTS OR
        //  - less than 200 ms PTS gaps (timeScale/5)
        contiguous |= inputSamples.length && nextAvcDts && (accurateTimeOffset && Math.abs(timeOffset - nextAvcDts / timeScale) < 0.1 || Math.abs(inputSamples[0].pts - nextAvcDts - initDTS) < timeScale / 5);
      }

      if (!contiguous) {
        // if not contiguous, let's use target timeOffset
        nextAvcDts = timeOffset * timeScale;
      }

      // PTS is coded on 33bits, and can loop from -2^32 to 2^32
      // ptsNormalize will make PTS/DTS value monotonic, we use last known DTS value as reference value
      inputSamples.forEach(function (sample) {
        sample.pts = ptsNormalize(sample.pts - initDTS, nextAvcDts);
        sample.dts = ptsNormalize(sample.dts - initDTS, nextAvcDts);
      });

      // sort video samples by DTS then PTS then demux id order
      inputSamples.sort(function (a, b) {
        var deltadts = a.dts - b.dts;
        var deltapts = a.pts - b.pts;
        return deltadts ? deltadts : deltapts ? deltapts : a.id - b.id;
      });

      // handle broken streams with PTS < DTS, tolerance up 200ms (18000 in 90kHz timescale)
      var PTSDTSshift = inputSamples.reduce(function (prev, curr) {
        return Math.max(Math.min(prev, curr.pts - curr.dts), -18000);
      }, 0);
      if (PTSDTSshift < 0) {
        _logger.logger.warn('PTS < DTS detected in video samples, shifting DTS by ' + Math.round(PTSDTSshift / 90) + ' ms to overcome this issue');
        for (var i = 0; i < inputSamples.length; i++) {
          inputSamples[i].dts += PTSDTSshift;
        }
      }

      // compute first DTS and last DTS, normalize them against reference value
      var sample = inputSamples[0];
      firstDTS = Math.max(sample.dts, 0);
      firstPTS = Math.max(sample.pts, 0);

      // check timestamp continuity accross consecutive fragments (this is to remove inter-fragment gap/hole)
      var delta = Math.round((firstDTS - nextAvcDts) / 90);
      // if fragment are contiguous, detect hole/overlapping between fragments
      if (contiguous) {
        if (delta) {
          if (delta > 1) {
            _logger.logger.log('AVC:' + delta + ' ms hole between fragments detected,filling it');
          } else if (delta < -1) {
            _logger.logger.log('AVC:' + -delta + ' ms overlapping between fragments detected');
          }
          // remove hole/gap : set DTS to next expected DTS
          firstDTS = nextAvcDts;
          inputSamples[0].dts = firstDTS;
          // offset PTS as well, ensure that PTS is smaller or equal than new DTS
          firstPTS = Math.max(firstPTS - delta, nextAvcDts);
          inputSamples[0].pts = firstPTS;
          _logger.logger.log('Video/PTS/DTS adjusted: ' + Math.round(firstPTS / 90) + '/' + Math.round(firstDTS / 90) + ',delta:' + delta + ' ms');
        }
      }
      nextDTS = firstDTS;

      // compute lastPTS/lastDTS
      sample = inputSamples[inputSamples.length - 1];
      lastDTS = Math.max(sample.dts, 0);
      lastPTS = Math.max(sample.pts, 0, lastDTS);

      // on Safari let's signal the same sample duration for all samples
      // sample duration (as expected by trun MP4 boxes), should be the delta between sample DTS
      // set this constant duration as being the avg delta between consecutive DTS.
      if (isSafari) {
        mp4SampleDuration = Math.round((lastDTS - firstDTS) / (inputSamples.length - 1));
      }

      var nbNalu = 0,
          naluLen = 0;
      for (var _i = 0; _i < nbSamples; _i++) {
        // compute total/avc sample length and nb of NAL units
        var _sample = inputSamples[_i],
            units = _sample.units,
            nbUnits = units.length,
            sampleLen = 0;
        for (var j = 0; j < nbUnits; j++) {
          sampleLen += units[j].data.length;
        }
        naluLen += sampleLen;
        nbNalu += nbUnits;
        _sample.length = sampleLen;

        // normalize PTS/DTS
        if (isSafari) {
          // sample DTS is computed using a constant decoding offset (mp4SampleDuration) between samples
          _sample.dts = firstDTS + _i * mp4SampleDuration;
        } else {
          // ensure sample monotonic DTS
          _sample.dts = Math.max(_sample.dts, firstDTS);
        }
        // ensure that computed value is greater or equal than sample DTS
        _sample.pts = Math.max(_sample.pts, _sample.dts);
      }

      /* concatenate the video data and construct the mdat in place
        (need 8 more bytes to fill length and mpdat type) */
      var mdatSize = naluLen + 4 * nbNalu + 8;
      try {
        mdat = new Uint8Array(mdatSize);
      } catch (err) {
        this.observer.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MUX_ERROR, details: _errors.ErrorDetails.REMUX_ALLOC_ERROR, fatal: false, bytes: mdatSize, reason: 'fail allocating video mdat ' + mdatSize });
        return;
      }
      var view = new DataView(mdat.buffer);
      view.setUint32(0, mdatSize);
      mdat.set(_mp4Generator2.default.types.mdat, 4);

      for (var _i2 = 0; _i2 < nbSamples; _i2++) {
        var avcSample = inputSamples[_i2],
            avcSampleUnits = avcSample.units,
            mp4SampleLength = 0,
            compositionTimeOffset = void 0;
        // convert NALU bitstream to MP4 format (prepend NALU with size field)
        for (var _j = 0, _nbUnits = avcSampleUnits.length; _j < _nbUnits; _j++) {
          var unit = avcSampleUnits[_j],
              unitData = unit.data,
              unitDataLen = unit.data.byteLength;
          view.setUint32(offset, unitDataLen);
          offset += 4;
          mdat.set(unitData, offset);
          offset += unitDataLen;
          mp4SampleLength += 4 + unitDataLen;
        }

        if (!isSafari) {
          // expected sample duration is the Decoding Timestamp diff of consecutive samples
          if (_i2 < nbSamples - 1) {
            mp4SampleDuration = inputSamples[_i2 + 1].dts - avcSample.dts;
          } else {
            var config = this.config,
                lastFrameDuration = avcSample.dts - inputSamples[_i2 > 0 ? _i2 - 1 : _i2].dts;
            if (config.stretchShortVideoTrack) {
              // In some cases, a segment's audio track duration may exceed the video track duration.
              // Since we've already remuxed audio, and we know how long the audio track is, we look to
              // see if the delta to the next segment is longer than the minimum of maxBufferHole and
              // maxSeekHole. If so, playback would potentially get stuck, so we artificially inflate
              // the duration of the last frame to minimize any potential gap between segments.
              var maxBufferHole = config.maxBufferHole,
                  maxSeekHole = config.maxSeekHole,
                  gapTolerance = Math.floor(Math.min(maxBufferHole, maxSeekHole) * timeScale),
                  deltaToFrameEnd = (audioTrackLength ? firstPTS + audioTrackLength * timeScale : this.nextAudioPts) - avcSample.pts;
              if (deltaToFrameEnd > gapTolerance) {
                // We subtract lastFrameDuration from deltaToFrameEnd to try to prevent any video
                // frame overlap. maxBufferHole/maxSeekHole should be >> lastFrameDuration anyway.
                mp4SampleDuration = deltaToFrameEnd - lastFrameDuration;
                if (mp4SampleDuration < 0) {
                  mp4SampleDuration = lastFrameDuration;
                }
                _logger.logger.log('It is approximately ' + deltaToFrameEnd / 90 + ' ms to the next segment; using duration ' + mp4SampleDuration / 90 + ' ms for the last video frame.');
              } else {
                mp4SampleDuration = lastFrameDuration;
              }
            } else {
              mp4SampleDuration = lastFrameDuration;
            }
          }
          compositionTimeOffset = Math.round(avcSample.pts - avcSample.dts);
        } else {
          compositionTimeOffset = Math.max(0, mp4SampleDuration * Math.round((avcSample.pts - avcSample.dts) / mp4SampleDuration));
        }

        //console.log('PTS/DTS/initDTS/normPTS/normDTS/relative PTS : ${avcSample.pts}/${avcSample.dts}/${initDTS}/${ptsnorm}/${dtsnorm}/${(avcSample.pts/4294967296).toFixed(3)}');
        outputSamples.push({
          size: mp4SampleLength,
          // constant duration
          duration: mp4SampleDuration,
          cts: compositionTimeOffset,
          flags: {
            isLeading: 0,
            isDependedOn: 0,
            hasRedundancy: 0,
            degradPrio: 0,
            dependsOn: avcSample.key ? 2 : 1,
            isNonSync: avcSample.key ? 0 : 1
          }
        });
      }
      // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
      this.nextAvcDts = lastDTS + mp4SampleDuration;
      var dropped = track.dropped;
      track.len = 0;
      track.nbNalu = 0;
      track.dropped = 0;
      if (outputSamples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
        var flags = outputSamples[0].flags;
        // chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue
        // https://code.google.com/p/chromium/issues/detail?id=229412
        flags.dependsOn = 2;
        flags.isNonSync = 0;
      }
      track.samples = outputSamples;
      moof = _mp4Generator2.default.moof(track.sequenceNumber++, firstDTS, track);
      track.samples = [];

      var data = {
        data1: moof,
        data2: mdat,
        startPTS: firstPTS / timeScale,
        endPTS: (lastPTS + mp4SampleDuration) / timeScale,
        startDTS: firstDTS / timeScale,
        endDTS: this.nextAvcDts / timeScale,
        type: 'video',
        nb: outputSamples.length,
        dropped: dropped
      };
      this.observer.trigger(_events2.default.FRAG_PARSING_DATA, data);
      return data;
    }
  }, {
    key: 'remuxAudio',
    value: function remuxAudio(track, timeOffset, contiguous, accurateTimeOffset) {
      var inputTimeScale = track.inputTimeScale,
          mp4timeScale = track.timescale,
          scaleFactor = inputTimeScale / mp4timeScale,
          mp4SampleDuration = track.isAAC ? 1024 : 1152,
          inputSampleDuration = mp4SampleDuration * scaleFactor,
          ptsNormalize = this._PTSNormalize,
          initDTS = this._initDTS,
          rawMPEG = !track.isAAC && this.typeSupported.mpeg;

      var offset,
          mp4Sample,
          fillFrame,
          mdat,
          moof,
          firstPTS,
          lastPTS,
          inputSamples = track.samples,
          outputSamples = [],
          nextAudioPts = this.nextAudioPts;

      // for audio samples, also consider consecutive fragments as being contiguous (even if a level switch occurs),
      // for sake of clarity:
      // consecutive fragments are frags with
      //  - less than 100ms gaps between new time offset (if accurate) and next expected PTS OR
      //  - less than 20 audio frames distance
      // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1)
      // this helps ensuring audio continuity
      // and this also avoids audio glitches/cut when switching quality, or reporting wrong duration on first audio frame
      contiguous |= inputSamples.length && nextAudioPts && (accurateTimeOffset && Math.abs(timeOffset - nextAudioPts / inputTimeScale) < 0.1 || Math.abs(inputSamples[0].pts - nextAudioPts - initDTS) < 20 * inputSampleDuration);

      if (!contiguous) {
        // if fragments are not contiguous, let's use timeOffset to compute next Audio PTS
        nextAudioPts = timeOffset * inputTimeScale;
      }

      // compute normalized PTS
      inputSamples.forEach(function (sample) {
        sample.pts = sample.dts = ptsNormalize(sample.pts - initDTS, nextAudioPts);
      });

      // sort based on normalized PTS (this is to avoid sorting issues in case timestamp
      // reloop in the middle of our samples array)
      inputSamples.sort(function (a, b) {
        return a.pts - b.pts;
      });

      // If the audio track is missing samples, the frames seem to get "left-shifted" within the
      // resulting mp4 segment, causing sync issues and leaving gaps at the end of the audio segment.
      // In an effort to prevent this from happening, we inject frames here where there are gaps.
      // When possible, we inject a silent frame; when that's not possible, we duplicate the last
      // frame.

      // only inject/drop audio frames in case time offset is accurate
      if (accurateTimeOffset && track.isAAC) {
        for (var i = 0, nextPts = nextAudioPts; i < inputSamples.length;) {
          // First, let's see how far off this frame is from where we expect it to be
          var sample = inputSamples[i],
              delta;
          var pts = sample.pts;
          delta = pts - nextPts;

          var duration = Math.abs(1000 * delta / inputTimeScale);

          // If we're overlapping by more than a duration, drop this sample
          if (delta <= -inputSampleDuration) {
            _logger.logger.warn('Dropping 1 audio frame @ ' + (nextPts / inputTimeScale).toFixed(3) + 's due to ' + duration + ' ms overlap.');
            inputSamples.splice(i, 1);
            track.len -= sample.unit.length;
            // Don't touch nextPtsNorm or i
          }

          // Insert missing frames if:
          // 1: We're more than one frame away
          // 2: Not more than MAX_SILENT_FRAME_DURATION away
          // 3: currentTime (aka nextPtsNorm) is not 0
          else if (delta >= inputSampleDuration && duration < MAX_SILENT_FRAME_DURATION && nextPts) {
              var missing = Math.round(delta / inputSampleDuration);
              _logger.logger.warn('Injecting ' + missing + ' audio frame @ ' + (nextPts / inputTimeScale).toFixed(3) + 's due to ' + Math.round(1000 * delta / inputTimeScale) + ' ms gap.');
              for (var j = 0; j < missing; j++) {
                var newStamp = Math.max(nextPts, 0);
                fillFrame = _aac2.default.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
                if (!fillFrame) {
                  _logger.logger.log('Unable to get silent frame for given audio codec; duplicating last frame instead.');
                  fillFrame = sample.unit.subarray();
                }
                inputSamples.splice(i, 0, { unit: fillFrame, pts: newStamp, dts: newStamp });
                track.len += fillFrame.length;
                nextPts += inputSampleDuration;
                i++;
              }

              // Adjust sample to next expected pts
              sample.pts = sample.dts = nextPts;
              nextPts += inputSampleDuration;
              i++;
            } else {
              // Otherwise, just adjust pts
              if (Math.abs(delta) > 0.1 * inputSampleDuration) {
                //logger.log(`Invalid frame delta ${Math.round(delta + inputSampleDuration)} at PTS ${Math.round(pts / 90)} (should be ${Math.round(inputSampleDuration)}).`);
              }
              sample.pts = sample.dts = nextPts;
              nextPts += inputSampleDuration;
              i++;
            }
        }
      }

      for (var _j2 = 0, _nbSamples = inputSamples.length; _j2 < _nbSamples; _j2++) {
        var audioSample = inputSamples[_j2];
        var unit = audioSample.unit;
        var _pts = audioSample.pts;
        //logger.log(`Audio/PTS:${Math.round(pts/90)}`);
        // if not first sample
        if (lastPTS !== undefined) {
          mp4Sample.duration = Math.round((_pts - lastPTS) / scaleFactor);
        } else {
          var _delta = Math.round(1000 * (_pts - nextAudioPts) / inputTimeScale),
              numMissingFrames = 0;
          // if fragment are contiguous, detect hole/overlapping between fragments
          // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1)
          if (contiguous && track.isAAC) {
            // log delta
            if (_delta) {
              if (_delta > 0 && _delta < MAX_SILENT_FRAME_DURATION) {
                numMissingFrames = Math.round((_pts - nextAudioPts) / inputSampleDuration);
                _logger.logger.log(_delta + ' ms hole between AAC samples detected,filling it');
                if (numMissingFrames > 0) {
                  fillFrame = _aac2.default.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
                  if (!fillFrame) {
                    fillFrame = unit.subarray();
                  }
                  track.len += numMissingFrames * fillFrame.length;
                }
                // if we have frame overlap, overlapping for more than half a frame duraion
              } else if (_delta < -12) {
                // drop overlapping audio frames... browser will deal with it
                _logger.logger.log('drop overlapping AAC sample, expected/parsed/delta:' + (nextAudioPts / inputTimeScale).toFixed(3) + 's/' + (_pts / inputTimeScale).toFixed(3) + 's/' + -_delta + 'ms');
                track.len -= unit.byteLength;
                continue;
              }
              // set PTS/DTS to expected PTS/DTS
              _pts = nextAudioPts;
            }
          }
          // remember first PTS of our audioSamples, ensure value is positive
          firstPTS = Math.max(0, _pts);
          if (track.len > 0) {
            /* concatenate the audio data and construct the mdat in place
              (need 8 more bytes to fill length and mdat type) */
            var mdatSize = rawMPEG ? track.len : track.len + 8;
            offset = rawMPEG ? 0 : 8;
            try {
              mdat = new Uint8Array(mdatSize);
            } catch (err) {
              this.observer.trigger(_events2.default.ERROR, { type: _errors.ErrorTypes.MUX_ERROR, details: _errors.ErrorDetails.REMUX_ALLOC_ERROR, fatal: false, bytes: mdatSize, reason: 'fail allocating audio mdat ' + mdatSize });
              return;
            }
            if (!rawMPEG) {
              var view = new DataView(mdat.buffer);
              view.setUint32(0, mdatSize);
              mdat.set(_mp4Generator2.default.types.mdat, 4);
            }
          } else {
            // no audio samples
            return;
          }
          for (var _i3 = 0; _i3 < numMissingFrames; _i3++) {
            fillFrame = _aac2.default.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);
            if (!fillFrame) {
              _logger.logger.log('Unable to get silent frame for given audio codec; duplicating this frame instead.');
              fillFrame = unit.subarray();
            }
            mdat.set(fillFrame, offset);
            offset += fillFrame.byteLength;
            mp4Sample = {
              size: fillFrame.byteLength,
              cts: 0,
              duration: 1024,
              flags: {
                isLeading: 0,
                isDependedOn: 0,
                hasRedundancy: 0,
                degradPrio: 0,
                dependsOn: 1
              }
            };
            outputSamples.push(mp4Sample);
          }
        }
        mdat.set(unit, offset);
        var unitLen = unit.byteLength;
        offset += unitLen;
        //console.log('PTS/DTS/initDTS/normPTS/normDTS/relative PTS : ${audioSample.pts}/${audioSample.dts}/${initDTS}/${ptsnorm}/${dtsnorm}/${(audioSample.pts/4294967296).toFixed(3)}');
        mp4Sample = {
          size: unitLen,
          cts: 0,
          duration: 0,
          flags: {
            isLeading: 0,
            isDependedOn: 0,
            hasRedundancy: 0,
            degradPrio: 0,
            dependsOn: 1
          }
        };
        outputSamples.push(mp4Sample);
        lastPTS = _pts;
      }
      var lastSampleDuration = 0;
      var nbSamples = outputSamples.length;
      //set last sample duration as being identical to previous sample
      if (nbSamples >= 2) {
        lastSampleDuration = outputSamples[nbSamples - 2].duration;
        mp4Sample.duration = lastSampleDuration;
      }
      if (nbSamples) {
        // next audio sample PTS should be equal to last sample PTS + duration
        this.nextAudioPts = nextAudioPts = lastPTS + scaleFactor * lastSampleDuration;
        //logger.log('Audio/PTS/PTSend:' + audioSample.pts.toFixed(0) + '/' + this.nextAacDts.toFixed(0));
        track.len = 0;
        track.samples = outputSamples;
        if (rawMPEG) {
          moof = new Uint8Array();
        } else {
          moof = _mp4Generator2.default.moof(track.sequenceNumber++, firstPTS / scaleFactor, track);
        }
        track.samples = [];
        var start = firstPTS / inputTimeScale;
        var end = nextAudioPts / inputTimeScale;
        var audioData = {
          data1: moof,
          data2: mdat,
          startPTS: start,
          endPTS: end,
          startDTS: start,
          endDTS: end,
          type: 'audio',
          nb: nbSamples
        };
        this.observer.trigger(_events2.default.FRAG_PARSING_DATA, audioData);
        return audioData;
      }
      return null;
    }
  }, {
    key: 'remuxEmptyAudio',
    value: function remuxEmptyAudio(track, timeOffset, contiguous, videoData) {
      var inputTimeScale = track.inputTimeScale,
          mp4timeScale = track.samplerate ? track.samplerate : inputTimeScale,
          scaleFactor = inputTimeScale / mp4timeScale,
          nextAudioPts = this.nextAudioPts,


      // sync with video's timestamp
      startDTS = (nextAudioPts !== undefined ? nextAudioPts : videoData.startDTS * inputTimeScale) + this._initDTS,
          endDTS = videoData.endDTS * inputTimeScale + this._initDTS,

      // one sample's duration value
      sampleDuration = 1024,
          frameDuration = scaleFactor * sampleDuration,


      // samples count of this segment's duration
      nbSamples = Math.ceil((endDTS - startDTS) / frameDuration),


      // silent frame
      silentFrame = _aac2.default.getSilentFrame(track.manifestCodec || track.codec, track.channelCount);

      _logger.logger.warn('remux empty Audio');
      // Can't remux if we can't generate a silent frame...
      if (!silentFrame) {
        _logger.logger.trace('Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec!');
        return;
      }

      var samples = [];
      for (var i = 0; i < nbSamples; i++) {
        var stamp = startDTS + i * frameDuration;
        samples.push({ unit: silentFrame, pts: stamp, dts: stamp });
        track.len += silentFrame.length;
      }
      track.samples = samples;

      this.remuxAudio(track, timeOffset, contiguous);
    }
  }, {
    key: 'remuxID3',
    value: function remuxID3(track, timeOffset) {
      var length = track.samples.length,
          sample;
      var inputTimeScale = track.inputTimeScale;
      var initPTS = this._initPTS;
      var initDTS = this._initDTS;
      // consume samples
      if (length) {
        for (var index = 0; index < length; index++) {
          sample = track.samples[index];
          // setting id3 pts, dts to relative time
          // using this._initPTS and this._initDTS to calculate relative time
          sample.pts = (sample.pts - initPTS) / inputTimeScale;
          sample.dts = (sample.dts - initDTS) / inputTimeScale;
        }
        this.observer.trigger(_events2.default.FRAG_PARSING_METADATA, {
          samples: track.samples
        });
      }

      track.samples = [];
      timeOffset = timeOffset;
    }
  }, {
    key: 'remuxText',
    value: function remuxText(track, timeOffset) {
      track.samples.sort(function (a, b) {
        return a.pts - b.pts;
      });

      var length = track.samples.length,
          sample;
      var inputTimeScale = track.inputTimeScale;
      var initPTS = this._initPTS;
      // consume samples
      if (length) {
        for (var index = 0; index < length; index++) {
          sample = track.samples[index];
          // setting text pts, dts to relative time
          // using this._initPTS and this._initDTS to calculate relative time
          sample.pts = (sample.pts - initPTS) / inputTimeScale;
        }
        this.observer.trigger(_events2.default.FRAG_PARSING_USERDATA, {
          samples: track.samples
        });
      }

      track.samples = [];
      timeOffset = timeOffset;
    }
  }, {
    key: '_PTSNormalize',
    value: function _PTSNormalize(value, reference) {
      var offset;
      if (reference === undefined) {
        return value;
      }
      if (reference < value) {
        // - 2^33
        offset = -8589934592;
      } else {
        // + 2^33
        offset = 8589934592;
      }
      /* PTS is 33bit (from 0 to 2^33 -1)
        if diff between value and reference is bigger than half of the amplitude (2^32) then it means that
        PTS looping occured. fill the gap */
      while (Math.abs(value - reference) > 4294967296) {
        value += offset;
      }
      return value;
    }
  }]);

  return MP4Remuxer;
}();

exports.default = MP4Remuxer;

},{"33":33,"35":35,"36":36,"44":44,"54":54}],46:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }(); /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * passthrough remuxer
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     */


var _events = _dereq_(35);

var _events2 = _interopRequireDefault(_events);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var PassThroughRemuxer = function () {
  function PassThroughRemuxer(observer) {
    _classCallCheck(this, PassThroughRemuxer);

    this.observer = observer;
  }

  _createClass(PassThroughRemuxer, [{
    key: 'destroy',
    value: function destroy() {}
  }, {
    key: 'resetTimeStamp',
    value: function resetTimeStamp() {}
  }, {
    key: 'resetInitSegment',
    value: function resetInitSegment() {}
  }, {
    key: 'remux',
    value: function remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset, rawData) {
      var observer = this.observer;
      var streamType = '';
      if (audioTrack) {
        streamType += 'audio';
      }
      if (videoTrack) {
        streamType += 'video';
      }
      observer.trigger(_events2.default.FRAG_PARSING_DATA, {
        data1: rawData,
        startPTS: timeOffset,
        startDTS: timeOffset,
        type: streamType,
        nb: 1,
        dropped: 0
      });
      //notify end of parsing
      observer.trigger(_events2.default.FRAG_PARSED);
    }
  }]);

  return PassThroughRemuxer;
}();

exports.default = PassThroughRemuxer;

},{"35":35}],47:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/;
var ATTR_LIST_REGEX = /\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g;

// adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js

var AttrList = function () {
  function AttrList(attrs) {
    _classCallCheck(this, AttrList);

    if (typeof attrs === 'string') {
      attrs = AttrList.parseAttrList(attrs);
    }
    for (var attr in attrs) {
      if (attrs.hasOwnProperty(attr)) {
        this[attr] = attrs[attr];
      }
    }
  }

  _createClass(AttrList, [{
    key: 'decimalInteger',
    value: function decimalInteger(attrName) {
      var intValue = parseInt(this[attrName], 10);
      if (intValue > Number.MAX_SAFE_INTEGER) {
        return Infinity;
      }
      return intValue;
    }
  }, {
    key: 'hexadecimalInteger',
    value: function hexadecimalInteger(attrName) {
      if (this[attrName]) {
        var stringValue = (this[attrName] || '0x').slice(2);
        stringValue = (stringValue.length & 1 ? '0' : '') + stringValue;

        var value = new Uint8Array(stringValue.length / 2);
        for (var i = 0; i < stringValue.length / 2; i++) {
          value[i] = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16);
        }
        return value;
      } else {
        return null;
      }
    }
  }, {
    key: 'hexadecimalIntegerAsNumber',
    value: function hexadecimalIntegerAsNumber(attrName) {
      var intValue = parseInt(this[attrName], 16);
      if (intValue > Number.MAX_SAFE_INTEGER) {
        return Infinity;
      }
      return intValue;
    }
  }, {
    key: 'decimalFloatingPoint',
    value: function decimalFloatingPoint(attrName) {
      return parseFloat(this[attrName]);
    }
  }, {
    key: 'enumeratedString',
    value: function enumeratedString(attrName) {
      return this[attrName];
    }
  }, {
    key: 'decimalResolution',
    value: function decimalResolution(attrName) {
      var res = DECIMAL_RESOLUTION_REGEX.exec(this[attrName]);
      if (res === null) {
        return undefined;
      }
      return {
        width: parseInt(res[1], 10),
        height: parseInt(res[2], 10)
      };
    }
  }], [{
    key: 'parseAttrList',
    value: function parseAttrList(input) {
      var match,
          attrs = {};
      ATTR_LIST_REGEX.lastIndex = 0;
      while ((match = ATTR_LIST_REGEX.exec(input)) !== null) {
        var value = match[2],
            quote = '"';

        if (value.indexOf(quote) === 0 && value.lastIndexOf(quote) === value.length - 1) {
          value = value.slice(1, -1);
        }
        attrs[match[1]] = value;
      }
      return attrs;
    }
  }]);

  return AttrList;
}();

exports.default = AttrList;

},{}],48:[function(_dereq_,module,exports){
"use strict";

var BinarySearch = {
    /**
     * Searches for an item in an array which matches a certain condition.
     * This requires the condition to only match one item in the array,
     * and for the array to be ordered.
     *
     * @param {Array} list The array to search.
     * @param {Function} comparisonFunction
     *      Called and provided a candidate item as the first argument.
     *      Should return:
     *          > -1 if the item should be located at a lower index than the provided item.
     *          > 1 if the item should be located at a higher index than the provided item.
     *          > 0 if the item is the item you're looking for.
     *
     * @return {*} The object if it is found or null otherwise.
     */
    search: function search(list, comparisonFunction) {
        var minIndex = 0;
        var maxIndex = list.length - 1;
        var currentIndex = null;
        var currentElement = null;

        while (minIndex <= maxIndex) {
            currentIndex = (minIndex + maxIndex) / 2 | 0;
            currentElement = list[currentIndex];

            var comparisonResult = comparisonFunction(currentElement);
            if (comparisonResult > 0) {
                minIndex = currentIndex + 1;
            } else if (comparisonResult < 0) {
                maxIndex = currentIndex - 1;
            } else {
                return currentElement;
            }
        }

        return null;
    }
};

module.exports = BinarySearch;

},{}],49:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
    value: true
});

var _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; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

/**
 *
 * This code was ported from the dash.js project at:
 *   https://github.com/Dash-Industry-Forum/dash.js/blob/development/externals/cea608-parser.js
 *   https://github.com/Dash-Industry-Forum/dash.js/commit/8269b26a761e0853bb21d78780ed945144ecdd4d#diff-71bc295a2d6b6b7093a1d3290d53a4b2
 *
 * The original copyright appears below:
 *
 * The copyright in this software is being made available under the BSD License,
 * included below. This software may be subject to other third party and contributor
 * rights, including patent rights, and no such rights are granted under this license.
 *
 * Copyright (c) 2015-2016, DASH Industry Forum.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *  1. Redistributions of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *  2. Neither the name of Dash Industry Forum nor the names of its
 *  contributors may be used to endorse or promote products derived from this software
 *  without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 *  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 *  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 *  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 *  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */
/**
 *  Exceptions from regular ASCII. CodePoints are mapped to UTF-16 codes
 */

var specialCea608CharsCodes = {
    0x2a: 0xe1, // lowercase a, acute accent
    0x5c: 0xe9, // lowercase e, acute accent
    0x5e: 0xed, // lowercase i, acute accent
    0x5f: 0xf3, // lowercase o, acute accent
    0x60: 0xfa, // lowercase u, acute accent
    0x7b: 0xe7, // lowercase c with cedilla
    0x7c: 0xf7, // division symbol
    0x7d: 0xd1, // uppercase N tilde
    0x7e: 0xf1, // lowercase n tilde
    0x7f: 0x2588, // Full block
    // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
    // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F
    // THIS MEANS THAT \x50 MUST BE ADDED TO THE VALUES
    0x80: 0xae, // Registered symbol (R)
    0x81: 0xb0, // degree sign
    0x82: 0xbd, // 1/2 symbol
    0x83: 0xbf, // Inverted (open) question mark
    0x84: 0x2122, // Trademark symbol (TM)
    0x85: 0xa2, // Cents symbol
    0x86: 0xa3, // Pounds sterling
    0x87: 0x266a, // Music 8'th note
    0x88: 0xe0, // lowercase a, grave accent
    0x89: 0x20, // transparent space (regular)
    0x8a: 0xe8, // lowercase e, grave accent
    0x8b: 0xe2, // lowercase a, circumflex accent
    0x8c: 0xea, // lowercase e, circumflex accent
    0x8d: 0xee, // lowercase i, circumflex accent
    0x8e: 0xf4, // lowercase o, circumflex accent
    0x8f: 0xfb, // lowercase u, circumflex accent
    // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
    // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F
    0x90: 0xc1, // capital letter A with acute
    0x91: 0xc9, // capital letter E with acute
    0x92: 0xd3, // capital letter O with acute
    0x93: 0xda, // capital letter U with acute
    0x94: 0xdc, // capital letter U with diaresis
    0x95: 0xfc, // lowercase letter U with diaeresis
    0x96: 0x2018, // opening single quote
    0x97: 0xa1, // inverted exclamation mark
    0x98: 0x2a, // asterisk
    0x99: 0x2019, // closing single quote
    0x9a: 0x2501, // box drawings heavy horizontal
    0x9b: 0xa9, // copyright sign
    0x9c: 0x2120, // Service mark
    0x9d: 0x2022, // (round) bullet
    0x9e: 0x201c, // Left double quotation mark
    0x9f: 0x201d, // Right double quotation mark
    0xa0: 0xc0, // uppercase A, grave accent
    0xa1: 0xc2, // uppercase A, circumflex
    0xa2: 0xc7, // uppercase C with cedilla
    0xa3: 0xc8, // uppercase E, grave accent
    0xa4: 0xca, // uppercase E, circumflex
    0xa5: 0xcb, // capital letter E with diaresis
    0xa6: 0xeb, // lowercase letter e with diaresis
    0xa7: 0xce, // uppercase I, circumflex
    0xa8: 0xcf, // uppercase I, with diaresis
    0xa9: 0xef, // lowercase i, with diaresis
    0xaa: 0xd4, // uppercase O, circumflex
    0xab: 0xd9, // uppercase U, grave accent
    0xac: 0xf9, // lowercase u, grave accent
    0xad: 0xdb, // uppercase U, circumflex
    0xae: 0xab, // left-pointing double angle quotation mark
    0xaf: 0xbb, // right-pointing double angle quotation mark
    // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS
    // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F
    0xb0: 0xc3, // Uppercase A, tilde
    0xb1: 0xe3, // Lowercase a, tilde
    0xb2: 0xcd, // Uppercase I, acute accent
    0xb3: 0xcc, // Uppercase I, grave accent
    0xb4: 0xec, // Lowercase i, grave accent
    0xb5: 0xd2, // Uppercase O, grave accent
    0xb6: 0xf2, // Lowercase o, grave accent
    0xb7: 0xd5, // Uppercase O, tilde
    0xb8: 0xf5, // Lowercase o, tilde
    0xb9: 0x7b, // Open curly brace
    0xba: 0x7d, // Closing curly brace
    0xbb: 0x5c, // Backslash
    0xbc: 0x5e, // Caret
    0xbd: 0x5f, // Underscore
    0xbe: 0x7c, // Pipe (vertical line)
    0xbf: 0x223c, // Tilde operator
    0xc0: 0xc4, // Uppercase A, umlaut
    0xc1: 0xe4, // Lowercase A, umlaut
    0xc2: 0xd6, // Uppercase O, umlaut
    0xc3: 0xf6, // Lowercase o, umlaut
    0xc4: 0xdf, // Esszett (sharp S)
    0xc5: 0xa5, // Yen symbol
    0xc6: 0xa4, // Generic currency sign
    0xc7: 0x2503, // Box drawings heavy vertical
    0xc8: 0xc5, // Uppercase A, ring
    0xc9: 0xe5, // Lowercase A, ring
    0xca: 0xd8, // Uppercase O, stroke
    0xcb: 0xf8, // Lowercase o, strok
    0xcc: 0x250f, // Box drawings heavy down and right
    0xcd: 0x2513, // Box drawings heavy down and left
    0xce: 0x2517, // Box drawings heavy up and right
    0xcf: 0x251b // Box drawings heavy up and left
};

/**
 * Utils
 */
var getCharForByte = function getCharForByte(byte) {
    var charCode = byte;
    if (specialCea608CharsCodes.hasOwnProperty(byte)) {
        charCode = specialCea608CharsCodes[byte];
    }
    return String.fromCharCode(charCode);
};

var NR_ROWS = 15,
    NR_COLS = 100;
// Tables to look up row from PAC data
var rowsLowCh1 = { 0x11: 1, 0x12: 3, 0x15: 5, 0x16: 7, 0x17: 9, 0x10: 11, 0x13: 12, 0x14: 14 };
var rowsHighCh1 = { 0x11: 2, 0x12: 4, 0x15: 6, 0x16: 8, 0x17: 10, 0x13: 13, 0x14: 15 };
var rowsLowCh2 = { 0x19: 1, 0x1A: 3, 0x1D: 5, 0x1E: 7, 0x1F: 9, 0x18: 11, 0x1B: 12, 0x1C: 14 };
var rowsHighCh2 = { 0x19: 2, 0x1A: 4, 0x1D: 6, 0x1E: 8, 0x1F: 10, 0x1B: 13, 0x1C: 15 };

var backgroundColors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'black', 'transparent'];

/**
 * Simple logger class to be able to write with time-stamps and filter on level.
 */
var logger = {
    verboseFilter: { 'DATA': 3, 'DEBUG': 3, 'INFO': 2, 'WARNING': 2, 'TEXT': 1, 'ERROR': 0 },
    time: null,
    verboseLevel: 0, // Only write errors
    setTime: function setTime(newTime) {
        this.time = newTime;
    },
    log: function log(severity, msg) {
        var minLevel = this.verboseFilter[severity];
        if (this.verboseLevel >= minLevel) {
            console.log(this.time + ' [' + severity + '] ' + msg);
        }
    }
};

var numArrayToHexArray = function numArrayToHexArray(numArray) {
    var hexArray = [];
    for (var j = 0; j < numArray.length; j++) {
        hexArray.push(numArray[j].toString(16));
    }
    return hexArray;
};

var PenState = function () {
    function PenState(foreground, underline, italics, background, flash) {
        _classCallCheck(this, PenState);

        this.foreground = foreground || 'white';
        this.underline = underline || false;
        this.italics = italics || false;
        this.background = background || 'black';
        this.flash = flash || false;
    }

    _createClass(PenState, [{
        key: 'reset',
        value: function reset() {
            this.foreground = 'white';
            this.underline = false;
            this.italics = false;
            this.background = 'black';
            this.flash = false;
        }
    }, {
        key: 'setStyles',
        value: function setStyles(styles) {
            var attribs = ['foreground', 'underline', 'italics', 'background', 'flash'];
            for (var i = 0; i < attribs.length; i++) {
                var style = attribs[i];
                if (styles.hasOwnProperty(style)) {
                    this[style] = styles[style];
                }
            }
        }
    }, {
        key: 'isDefault',
        value: function isDefault() {
            return this.foreground === 'white' && !this.underline && !this.italics && this.background === 'black' && !this.flash;
        }
    }, {
        key: 'equals',
        value: function equals(other) {
            return this.foreground === other.foreground && this.underline === other.underline && this.italics === other.italics && this.background === other.background && this.flash === other.flash;
        }
    }, {
        key: 'copy',
        value: function copy(newPenState) {
            this.foreground = newPenState.foreground;
            this.underline = newPenState.underline;
            this.italics = newPenState.italics;
            this.background = newPenState.background;
            this.flash = newPenState.flash;
        }
    }, {
        key: 'toString',
        value: function toString() {
            return 'color=' + this.foreground + ', underline=' + this.underline + ', italics=' + this.italics + ', background=' + this.background + ', flash=' + this.flash;
        }
    }]);

    return PenState;
}();

/**
 * Unicode character with styling and background.
 * @constructor
 */


var StyledUnicodeChar = function () {
    function StyledUnicodeChar(uchar, foreground, underline, italics, background, flash) {
        _classCallCheck(this, StyledUnicodeChar);

        this.uchar = uchar || ' '; // unicode character
        this.penState = new PenState(foreground, underline, italics, background, flash);
    }

    _createClass(StyledUnicodeChar, [{
        key: 'reset',
        value: function reset() {
            this.uchar = ' ';
            this.penState.reset();
        }
    }, {
        key: 'setChar',
        value: function setChar(uchar, newPenState) {
            this.uchar = uchar;
            this.penState.copy(newPenState);
        }
    }, {
        key: 'setPenState',
        value: function setPenState(newPenState) {
            this.penState.copy(newPenState);
        }
    }, {
        key: 'equals',
        value: function equals(other) {
            return this.uchar === other.uchar && this.penState.equals(other.penState);
        }
    }, {
        key: 'copy',
        value: function copy(newChar) {
            this.uchar = newChar.uchar;
            this.penState.copy(newChar.penState);
        }
    }, {
        key: 'isEmpty',
        value: function isEmpty() {
            return this.uchar === ' ' && this.penState.isDefault();
        }
    }]);

    return StyledUnicodeChar;
}();

/**
 * CEA-608 row consisting of NR_COLS instances of StyledUnicodeChar.
 * @constructor
 */


var Row = function () {
    function Row() {
        _classCallCheck(this, Row);

        this.chars = [];
        for (var i = 0; i < NR_COLS; i++) {
            this.chars.push(new StyledUnicodeChar());
        }
        this.pos = 0;
        this.currPenState = new PenState();
    }

    _createClass(Row, [{
        key: 'equals',
        value: function equals(other) {
            var equal = true;
            for (var i = 0; i < NR_COLS; i++) {
                if (!this.chars[i].equals(other.chars[i])) {
                    equal = false;
                    break;
                }
            }
            return equal;
        }
    }, {
        key: 'copy',
        value: function copy(other) {
            for (var i = 0; i < NR_COLS; i++) {
                this.chars[i].copy(other.chars[i]);
            }
        }
    }, {
        key: 'isEmpty',
        value: function isEmpty() {
            var empty = true;
            for (var i = 0; i < NR_COLS; i++) {
                if (!this.chars[i].isEmpty()) {
                    empty = false;
                    break;
                }
            }
            return empty;
        }

        /**
         *  Set the cursor to a valid column.
         */

    }, {
        key: 'setCursor',
        value: function setCursor(absPos) {
            if (this.pos !== absPos) {
                this.pos = absPos;
            }
            if (this.pos < 0) {
                logger.log('ERROR', 'Negative cursor position ' + this.pos);
                this.pos = 0;
            } else if (this.pos > NR_COLS) {
                logger.log('ERROR', 'Too large cursor position ' + this.pos);
                this.pos = NR_COLS;
            }
        }

        /**
         * Move the cursor relative to current position.
         */

    }, {
        key: 'moveCursor',
        value: function moveCursor(relPos) {
            var newPos = this.pos + relPos;
            if (relPos > 1) {
                for (var i = this.pos + 1; i < newPos + 1; i++) {
                    this.chars[i].setPenState(this.currPenState);
                }
            }
            this.setCursor(newPos);
        }

        /**
         * Backspace, move one step back and clear character.
         */

    }, {
        key: 'backSpace',
        value: function backSpace() {
            this.moveCursor(-1);
            this.chars[this.pos].setChar(' ', this.currPenState);
        }
    }, {
        key: 'insertChar',
        value: function insertChar(byte) {
            if (byte >= 0x90) {
                //Extended char
                this.backSpace();
            }
            var char = getCharForByte(byte);
            if (this.pos >= NR_COLS) {
                logger.log('ERROR', 'Cannot insert ' + byte.toString(16) + ' (' + char + ') at position ' + this.pos + '. Skipping it!');
                return;
            }
            this.chars[this.pos].setChar(char, this.currPenState);
            this.moveCursor(1);
        }
    }, {
        key: 'clearFromPos',
        value: function clearFromPos(startPos) {
            var i;
            for (i = startPos; i < NR_COLS; i++) {
                this.chars[i].reset();
            }
        }
    }, {
        key: 'clear',
        value: function clear() {
            this.clearFromPos(0);
            this.pos = 0;
            this.currPenState.reset();
        }
    }, {
        key: 'clearToEndOfRow',
        value: function clearToEndOfRow() {
            this.clearFromPos(this.pos);
        }
    }, {
        key: 'getTextString',
        value: function getTextString() {
            var chars = [];
            var empty = true;
            for (var i = 0; i < NR_COLS; i++) {
                var char = this.chars[i].uchar;
                if (char !== ' ') {
                    empty = false;
                }
                chars.push(char);
            }
            if (empty) {
                return '';
            } else {
                return chars.join('');
            }
        }
    }, {
        key: 'setPenStyles',
        value: function setPenStyles(styles) {
            this.currPenState.setStyles(styles);
            var currChar = this.chars[this.pos];
            currChar.setPenState(this.currPenState);
        }
    }]);

    return Row;
}();

/**
 * Keep a CEA-608 screen of 32x15 styled characters
 * @constructor
*/


var CaptionScreen = function () {
    function CaptionScreen() {
        _classCallCheck(this, CaptionScreen);

        this.rows = [];
        for (var i = 0; i < NR_ROWS; i++) {
            this.rows.push(new Row()); // Note that we use zero-based numbering (0-14)
        }
        this.currRow = NR_ROWS - 1;
        this.nrRollUpRows = null;
        this.reset();
    }

    _createClass(CaptionScreen, [{
        key: 'reset',
        value: function reset() {
            for (var i = 0; i < NR_ROWS; i++) {
                this.rows[i].clear();
            }
            this.currRow = NR_ROWS - 1;
        }
    }, {
        key: 'equals',
        value: function equals(other) {
            var equal = true;
            for (var i = 0; i < NR_ROWS; i++) {
                if (!this.rows[i].equals(other.rows[i])) {
                    equal = false;
                    break;
                }
            }
            return equal;
        }
    }, {
        key: 'copy',
        value: function copy(other) {
            for (var i = 0; i < NR_ROWS; i++) {
                this.rows[i].copy(other.rows[i]);
            }
        }
    }, {
        key: 'isEmpty',
        value: function isEmpty() {
            var empty = true;
            for (var i = 0; i < NR_ROWS; i++) {
                if (!this.rows[i].isEmpty()) {
                    empty = false;
                    break;
                }
            }
            return empty;
        }
    }, {
        key: 'backSpace',
        value: function backSpace() {
            var row = this.rows[this.currRow];
            row.backSpace();
        }
    }, {
        key: 'clearToEndOfRow',
        value: function clearToEndOfRow() {
            var row = this.rows[this.currRow];
            row.clearToEndOfRow();
        }

        /**
         * Insert a character (without styling) in the current row.
         */

    }, {
        key: 'insertChar',
        value: function insertChar(char) {
            var row = this.rows[this.currRow];
            row.insertChar(char);
        }
    }, {
        key: 'setPen',
        value: function setPen(styles) {
            var row = this.rows[this.currRow];
            row.setPenStyles(styles);
        }
    }, {
        key: 'moveCursor',
        value: function moveCursor(relPos) {
            var row = this.rows[this.currRow];
            row.moveCursor(relPos);
        }
    }, {
        key: 'setCursor',
        value: function setCursor(absPos) {
            logger.log('INFO', 'setCursor: ' + absPos);
            var row = this.rows[this.currRow];
            row.setCursor(absPos);
        }
    }, {
        key: 'setPAC',
        value: function setPAC(pacData) {
            logger.log('INFO', 'pacData = ' + JSON.stringify(pacData));
            var newRow = pacData.row - 1;
            if (this.nrRollUpRows && newRow < this.nrRollUpRows - 1) {
                newRow = this.nrRollUpRows - 1;
            }

            //Make sure this only affects Roll-up Captions by checking this.nrRollUpRows
            if (this.nrRollUpRows && this.currRow !== newRow) {
                //clear all rows first
                for (var i = 0; i < NR_ROWS; i++) {
                    this.rows[i].clear();
                }

                //Copy this.nrRollUpRows rows from lastOutputScreen and place it in the newRow location
                //topRowIndex - the start of rows to copy (inclusive index)
                var topRowIndex = this.currRow + 1 - this.nrRollUpRows;
                //We only copy if the last position was already shown.
                //We use the cueStartTime value to check this.
                var lastOutputScreen = this.lastOutputScreen;
                if (lastOutputScreen) {
                    var prevLineTime = lastOutputScreen.rows[topRowIndex].cueStartTime;
                    if (prevLineTime && prevLineTime < logger.time) {
                        for (var _i = 0; _i < this.nrRollUpRows; _i++) {
                            this.rows[newRow - this.nrRollUpRows + _i + 1].copy(lastOutputScreen.rows[topRowIndex + _i]);
                        }
                    }
                }
            }

            this.currRow = newRow;
            var row = this.rows[this.currRow];
            if (pacData.indent !== null) {
                var indent = pacData.indent;
                var prevPos = Math.max(indent - 1, 0);
                row.setCursor(pacData.indent);
                pacData.color = row.chars[prevPos].penState.foreground;
            }
            var styles = { foreground: pacData.color, underline: pacData.underline, italics: pacData.italics, background: 'black', flash: false };
            this.setPen(styles);
        }

        /**
         * Set background/extra foreground, but first do back_space, and then insert space (backwards compatibility).
         */

    }, {
        key: 'setBkgData',
        value: function setBkgData(bkgData) {

            logger.log('INFO', 'bkgData = ' + JSON.stringify(bkgData));
            this.backSpace();
            this.setPen(bkgData);
            this.insertChar(0x20); //Space
        }
    }, {
        key: 'setRollUpRows',
        value: function setRollUpRows(nrRows) {
            this.nrRollUpRows = nrRows;
        }
    }, {
        key: 'rollUp',
        value: function rollUp() {
            if (this.nrRollUpRows === null) {
                logger.log('DEBUG', 'roll_up but nrRollUpRows not set yet');
                return; //Not properly setup
            }
            logger.log('TEXT', this.getDisplayText());
            var topRowIndex = this.currRow + 1 - this.nrRollUpRows;
            var topRow = this.rows.splice(topRowIndex, 1)[0];
            topRow.clear();
            this.rows.splice(this.currRow, 0, topRow);
            logger.log('INFO', 'Rolling up');
            //logger.log('TEXT', this.get_display_text())
        }

        /**
         * Get all non-empty rows with as unicode text.
         */

    }, {
        key: 'getDisplayText',
        value: function getDisplayText(asOneRow) {
            asOneRow = asOneRow || false;
            var displayText = [];
            var text = '';
            var rowNr = -1;
            for (var i = 0; i < NR_ROWS; i++) {
                var rowText = this.rows[i].getTextString();
                if (rowText) {
                    rowNr = i + 1;
                    if (asOneRow) {
                        displayText.push('Row ' + rowNr + ': \'' + rowText + '\'');
                    } else {
                        displayText.push(rowText.trim());
                    }
                }
            }
            if (displayText.length > 0) {
                if (asOneRow) {
                    text = '[' + displayText.join(' | ') + ']';
                } else {
                    text = displayText.join('\n');
                }
            }
            return text;
        }
    }, {
        key: 'getTextAndFormat',
        value: function getTextAndFormat() {
            return this.rows;
        }
    }]);

    return CaptionScreen;
}();

//var modes = ['MODE_ROLL-UP', 'MODE_POP-ON', 'MODE_PAINT-ON', 'MODE_TEXT'];

var Cea608Channel = function () {
    function Cea608Channel(channelNumber, outputFilter) {
        _classCallCheck(this, Cea608Channel);

        this.chNr = channelNumber;
        this.outputFilter = outputFilter;
        this.mode = null;
        this.verbose = 0;
        this.displayedMemory = new CaptionScreen();
        this.nonDisplayedMemory = new CaptionScreen();
        this.lastOutputScreen = new CaptionScreen();
        this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1];
        this.writeScreen = this.displayedMemory;
        this.mode = null;
        this.cueStartTime = null; // Keeps track of where a cue started.
    }

    _createClass(Cea608Channel, [{
        key: 'reset',
        value: function reset() {
            this.mode = null;
            this.displayedMemory.reset();
            this.nonDisplayedMemory.reset();
            this.lastOutputScreen.reset();
            this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1];
            this.writeScreen = this.displayedMemory;
            this.mode = null;
            this.cueStartTime = null;
            this.lastCueEndTime = null;
        }
    }, {
        key: 'getHandler',
        value: function getHandler() {
            return this.outputFilter;
        }
    }, {
        key: 'setHandler',
        value: function setHandler(newHandler) {
            this.outputFilter = newHandler;
        }
    }, {
        key: 'setPAC',
        value: function setPAC(pacData) {
            this.writeScreen.setPAC(pacData);
        }
    }, {
        key: 'setBkgData',
        value: function setBkgData(bkgData) {
            this.writeScreen.setBkgData(bkgData);
        }
    }, {
        key: 'setMode',
        value: function setMode(newMode) {
            if (newMode === this.mode) {
                return;
            }
            this.mode = newMode;
            logger.log('INFO', 'MODE=' + newMode);
            if (this.mode === 'MODE_POP-ON') {
                this.writeScreen = this.nonDisplayedMemory;
            } else {
                this.writeScreen = this.displayedMemory;
                this.writeScreen.reset();
            }
            if (this.mode !== 'MODE_ROLL-UP') {
                this.displayedMemory.nrRollUpRows = null;
                this.nonDisplayedMemory.nrRollUpRows = null;
            }
            this.mode = newMode;
        }
    }, {
        key: 'insertChars',
        value: function insertChars(chars) {
            for (var i = 0; i < chars.length; i++) {
                this.writeScreen.insertChar(chars[i]);
            }
            var screen = this.writeScreen === this.displayedMemory ? 'DISP' : 'NON_DISP';
            logger.log('INFO', screen + ': ' + this.writeScreen.getDisplayText(true));
            if (this.mode === 'MODE_PAINT-ON' || this.mode === 'MODE_ROLL-UP') {
                logger.log('TEXT', 'DISPLAYED: ' + this.displayedMemory.getDisplayText(true));
                this.outputDataUpdate();
            }
        }
    }, {
        key: 'ccRCL',
        value: function ccRCL() {
            // Resume Caption Loading (switch mode to Pop On)
            logger.log('INFO', 'RCL - Resume Caption Loading');
            this.setMode('MODE_POP-ON');
        }
    }, {
        key: 'ccBS',
        value: function ccBS() {
            // BackSpace
            logger.log('INFO', 'BS - BackSpace');
            if (this.mode === 'MODE_TEXT') {
                return;
            }
            this.writeScreen.backSpace();
            if (this.writeScreen === this.displayedMemory) {
                this.outputDataUpdate();
            }
        }
    }, {
        key: 'ccAOF',
        value: function ccAOF() {
            // Reserved (formerly Alarm Off)
            return;
        }
    }, {
        key: 'ccAON',
        value: function ccAON() {
            // Reserved (formerly Alarm On)
            return;
        }
    }, {
        key: 'ccDER',
        value: function ccDER() {
            // Delete to End of Row
            logger.log('INFO', 'DER- Delete to End of Row');
            this.writeScreen.clearToEndOfRow();
            this.outputDataUpdate();
        }
    }, {
        key: 'ccRU',
        value: function ccRU(nrRows) {
            //Roll-Up Captions-2,3,or 4 Rows
            logger.log('INFO', 'RU(' + nrRows + ') - Roll Up');
            this.writeScreen = this.displayedMemory;
            this.setMode('MODE_ROLL-UP');
            this.writeScreen.setRollUpRows(nrRows);
        }
    }, {
        key: 'ccFON',
        value: function ccFON() {
            //Flash On
            logger.log('INFO', 'FON - Flash On');
            this.writeScreen.setPen({ flash: true });
        }
    }, {
        key: 'ccRDC',
        value: function ccRDC() {
            // Resume Direct Captioning (switch mode to PaintOn)
            logger.log('INFO', 'RDC - Resume Direct Captioning');
            this.setMode('MODE_PAINT-ON');
        }
    }, {
        key: 'ccTR',
        value: function ccTR() {
            // Text Restart in text mode (not supported, however)
            logger.log('INFO', 'TR');
            this.setMode('MODE_TEXT');
        }
    }, {
        key: 'ccRTD',
        value: function ccRTD() {
            // Resume Text Display in Text mode (not supported, however)
            logger.log('INFO', 'RTD');
            this.setMode('MODE_TEXT');
        }
    }, {
        key: 'ccEDM',
        value: function ccEDM() {
            // Erase Displayed Memory
            logger.log('INFO', 'EDM - Erase Displayed Memory');
            this.displayedMemory.reset();
            this.outputDataUpdate();
        }
    }, {
        key: 'ccCR',
        value: function ccCR() {
            // Carriage Return
            logger.log('CR - Carriage Return');
            this.writeScreen.rollUp();
            this.outputDataUpdate();
        }
    }, {
        key: 'ccENM',
        value: function ccENM() {
            //Erase Non-Displayed Memory
            logger.log('INFO', 'ENM - Erase Non-displayed Memory');
            this.nonDisplayedMemory.reset();
        }
    }, {
        key: 'ccEOC',
        value: function ccEOC() {
            //End of Caption (Flip Memories)
            logger.log('INFO', 'EOC - End Of Caption');
            if (this.mode === 'MODE_POP-ON') {
                var tmp = this.displayedMemory;
                this.displayedMemory = this.nonDisplayedMemory;
                this.nonDisplayedMemory = tmp;
                this.writeScreen = this.nonDisplayedMemory;
                logger.log('TEXT', 'DISP: ' + this.displayedMemory.getDisplayText());
            }
            this.outputDataUpdate();
        }
    }, {
        key: 'ccTO',
        value: function ccTO(nrCols) {
            // Tab Offset 1,2, or 3 columns
            logger.log('INFO', 'TO(' + nrCols + ') - Tab Offset');
            this.writeScreen.moveCursor(nrCols);
        }
    }, {
        key: 'ccMIDROW',
        value: function ccMIDROW(secondByte) {
            // Parse MIDROW command
            var styles = { flash: false };
            styles.underline = secondByte % 2 === 1;
            styles.italics = secondByte >= 0x2e;
            if (!styles.italics) {
                var colorIndex = Math.floor(secondByte / 2) - 0x10;
                var colors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta'];
                styles.foreground = colors[colorIndex];
            } else {
                styles.foreground = 'white';
            }
            logger.log('INFO', 'MIDROW: ' + JSON.stringify(styles));
            this.writeScreen.setPen(styles);
        }
    }, {
        key: 'outputDataUpdate',
        value: function outputDataUpdate() {
            var t = logger.time;
            if (t === null) {
                return;
            }
            if (this.outputFilter) {
                if (this.outputFilter.updateData) {
                    this.outputFilter.updateData(t, this.displayedMemory);
                }
                if (this.cueStartTime === null && !this.displayedMemory.isEmpty()) {
                    // Start of a new cue
                    this.cueStartTime = t;
                } else {
                    if (!this.displayedMemory.equals(this.lastOutputScreen)) {
                        if (this.outputFilter.newCue) {
                            this.outputFilter.newCue(this.cueStartTime, t, this.lastOutputScreen);
                        }
                        this.cueStartTime = this.displayedMemory.isEmpty() ? null : t;
                    }
                }
                this.lastOutputScreen.copy(this.displayedMemory);
            }
        }
    }, {
        key: 'cueSplitAtTime',
        value: function cueSplitAtTime(t) {
            if (this.outputFilter) {
                if (!this.displayedMemory.isEmpty()) {
                    if (this.outputFilter.newCue) {
                        this.outputFilter.newCue(this.cueStartTime, t, this.displayedMemory);
                    }
                    this.cueStartTime = t;
                }
            }
        }
    }]);

    return Cea608Channel;
}();

var Cea608Parser = function () {
    function Cea608Parser(field, out1, out2) {
        _classCallCheck(this, Cea608Parser);

        this.field = field || 1;
        this.outputs = [out1, out2];
        this.channels = [new Cea608Channel(1, out1), new Cea608Channel(2, out2)];
        this.currChNr = -1; // Will be 1 or 2
        this.lastCmdA = null; // First byte of last command
        this.lastCmdB = null; // Second byte of last command
        this.bufferedData = [];
        this.startTime = null;
        this.lastTime = null;
        this.dataCounters = { 'padding': 0, 'char': 0, 'cmd': 0, 'other': 0 };
    }

    _createClass(Cea608Parser, [{
        key: 'getHandler',
        value: function getHandler(index) {
            return this.channels[index].getHandler();
        }
    }, {
        key: 'setHandler',
        value: function setHandler(index, newHandler) {
            this.channels[index].setHandler(newHandler);
        }

        /**
         * Add data for time t in forms of list of bytes (unsigned ints). The bytes are treated as pairs.
         */

    }, {
        key: 'addData',
        value: function addData(t, byteList) {
            var cmdFound,
                a,
                b,
                charsFound = false;

            this.lastTime = t;
            logger.setTime(t);

            for (var i = 0; i < byteList.length; i += 2) {
                a = byteList[i] & 0x7f;
                b = byteList[i + 1] & 0x7f;
                if (a === 0 && b === 0) {
                    this.dataCounters.padding += 2;
                    continue;
                } else {
                    logger.log('DATA', '[' + numArrayToHexArray([byteList[i], byteList[i + 1]]) + '] -> (' + numArrayToHexArray([a, b]) + ')');
                }
                cmdFound = this.parseCmd(a, b);
                if (!cmdFound) {
                    cmdFound = this.parseMidrow(a, b);
                }
                if (!cmdFound) {
                    cmdFound = this.parsePAC(a, b);
                }
                if (!cmdFound) {
                    cmdFound = this.parseBackgroundAttributes(a, b);
                }
                if (!cmdFound) {
                    charsFound = this.parseChars(a, b);
                    if (charsFound) {
                        if (this.currChNr && this.currChNr >= 0) {
                            var channel = this.channels[this.currChNr - 1];
                            channel.insertChars(charsFound);
                        } else {
                            logger.log('WARNING', 'No channel found yet. TEXT-MODE?');
                        }
                    }
                }
                if (cmdFound) {
                    this.dataCounters.cmd += 2;
                } else if (charsFound) {
                    this.dataCounters.char += 2;
                } else {
                    this.dataCounters.other += 2;
                    logger.log('WARNING', 'Couldn\'t parse cleaned data ' + numArrayToHexArray([a, b]) + ' orig: ' + numArrayToHexArray([byteList[i], byteList[i + 1]]));
                }
            }
        }

        /**
         * Parse Command.
         * @returns {Boolean} Tells if a command was found
         */

    }, {
        key: 'parseCmd',
        value: function parseCmd(a, b) {
            var chNr = null;

            var cond1 = (a === 0x14 || a === 0x1C) && 0x20 <= b && b <= 0x2F;
            var cond2 = (a === 0x17 || a === 0x1F) && 0x21 <= b && b <= 0x23;
            if (!(cond1 || cond2)) {
                return false;
            }

            if (a === this.lastCmdA && b === this.lastCmdB) {
                this.lastCmdA = null;
                this.lastCmdB = null; // Repeated commands are dropped (once)
                logger.log('DEBUG', 'Repeated command (' + numArrayToHexArray([a, b]) + ') is dropped');
                return true;
            }

            if (a === 0x14 || a === 0x17) {
                chNr = 1;
            } else {
                chNr = 2; // (a === 0x1C || a=== 0x1f)
            }

            var channel = this.channels[chNr - 1];

            if (a === 0x14 || a === 0x1C) {
                if (b === 0x20) {
                    channel.ccRCL();
                } else if (b === 0x21) {
                    channel.ccBS();
                } else if (b === 0x22) {
                    channel.ccAOF();
                } else if (b === 0x23) {
                    channel.ccAON();
                } else if (b === 0x24) {
                    channel.ccDER();
                } else if (b === 0x25) {
                    channel.ccRU(2);
                } else if (b === 0x26) {
                    channel.ccRU(3);
                } else if (b === 0x27) {
                    channel.ccRU(4);
                } else if (b === 0x28) {
                    channel.ccFON();
                } else if (b === 0x29) {
                    channel.ccRDC();
                } else if (b === 0x2A) {
                    channel.ccTR();
                } else if (b === 0x2B) {
                    channel.ccRTD();
                } else if (b === 0x2C) {
                    channel.ccEDM();
                } else if (b === 0x2D) {
                    channel.ccCR();
                } else if (b === 0x2E) {
                    channel.ccENM();
                } else if (b === 0x2F) {
                    channel.ccEOC();
                }
            } else {
                //a == 0x17 || a == 0x1F
                channel.ccTO(b - 0x20);
            }
            this.lastCmdA = a;
            this.lastCmdB = b;
            this.currChNr = chNr;
            return true;
        }

        /**
         * Parse midrow styling command
         * @returns {Boolean}
         */

    }, {
        key: 'parseMidrow',
        value: function parseMidrow(a, b) {
            var chNr = null;

            if ((a === 0x11 || a === 0x19) && 0x20 <= b && b <= 0x2f) {
                if (a === 0x11) {
                    chNr = 1;
                } else {
                    chNr = 2;
                }
                if (chNr !== this.currChNr) {
                    logger.log('ERROR', 'Mismatch channel in midrow parsing');
                    return false;
                }
                var channel = this.channels[chNr - 1];
                channel.ccMIDROW(b);
                logger.log('DEBUG', 'MIDROW (' + numArrayToHexArray([a, b]) + ')');
                return true;
            }
            return false;
        }
        /**
         * Parse Preable Access Codes (Table 53).
         * @returns {Boolean} Tells if PAC found
         */

    }, {
        key: 'parsePAC',
        value: function parsePAC(a, b) {

            var chNr = null;
            var row = null;

            var case1 = (0x11 <= a && a <= 0x17 || 0x19 <= a && a <= 0x1F) && 0x40 <= b && b <= 0x7F;
            var case2 = (a === 0x10 || a === 0x18) && 0x40 <= b && b <= 0x5F;
            if (!(case1 || case2)) {
                return false;
            }

            if (a === this.lastCmdA && b === this.lastCmdB) {
                this.lastCmdA = null;
                this.lastCmdB = null;
                return true; // Repeated commands are dropped (once)
            }

            chNr = a <= 0x17 ? 1 : 2;

            if (0x40 <= b && b <= 0x5F) {
                row = chNr === 1 ? rowsLowCh1[a] : rowsLowCh2[a];
            } else {
                // 0x60 <= b <= 0x7F
                row = chNr === 1 ? rowsHighCh1[a] : rowsHighCh2[a];
            }
            var pacData = this.interpretPAC(row, b);
            var channel = this.channels[chNr - 1];
            channel.setPAC(pacData);
            this.lastCmdA = a;
            this.lastCmdB = b;
            this.currChNr = chNr;
            return true;
        }

        /**
         * Interpret the second byte of the pac, and return the information.
         * @returns {Object} pacData with style parameters.
         */

    }, {
        key: 'interpretPAC',
        value: function interpretPAC(row, byte) {
            var pacIndex = byte;
            var pacData = { color: null, italics: false, indent: null, underline: false, row: row };

            if (byte > 0x5F) {
                pacIndex = byte - 0x60;
            } else {
                pacIndex = byte - 0x40;
            }
            pacData.underline = (pacIndex & 1) === 1;
            if (pacIndex <= 0xd) {
                pacData.color = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'white'][Math.floor(pacIndex / 2)];
            } else if (pacIndex <= 0xf) {
                pacData.italics = true;
                pacData.color = 'white';
            } else {
                pacData.indent = Math.floor((pacIndex - 0x10) / 2) * 4;
            }
            return pacData; // Note that row has zero offset. The spec uses 1.
        }

        /**
         * Parse characters.
         * @returns An array with 1 to 2 codes corresponding to chars, if found. null otherwise.
         */

    }, {
        key: 'parseChars',
        value: function parseChars(a, b) {

            var channelNr = null,
                charCodes = null,
                charCode1 = null;

            if (a >= 0x19) {
                channelNr = 2;
                charCode1 = a - 8;
            } else {
                channelNr = 1;
                charCode1 = a;
            }
            if (0x11 <= charCode1 && charCode1 <= 0x13) {
                // Special character
                var oneCode = b;
                if (charCode1 === 0x11) {
                    oneCode = b + 0x50;
                } else if (charCode1 === 0x12) {
                    oneCode = b + 0x70;
                } else {
                    oneCode = b + 0x90;
                }
                logger.log('INFO', 'Special char \'' + getCharForByte(oneCode) + '\' in channel ' + channelNr);
                charCodes = [oneCode];
            } else if (0x20 <= a && a <= 0x7f) {
                charCodes = b === 0 ? [a] : [a, b];
            }
            if (charCodes) {
                var hexCodes = numArrayToHexArray(charCodes);
                logger.log('DEBUG', 'Char codes =  ' + hexCodes.join(','));
                this.lastCmdA = null;
                this.lastCmdB = null;
            }
            return charCodes;
        }

        /**
        * Parse extended background attributes as well as new foreground color black.
        * @returns{Boolean} Tells if background attributes are found
        */

    }, {
        key: 'parseBackgroundAttributes',
        value: function parseBackgroundAttributes(a, b) {
            var bkgData, index, chNr, channel;

            var case1 = (a === 0x10 || a === 0x18) && 0x20 <= b && b <= 0x2f;
            var case2 = (a === 0x17 || a === 0x1f) && 0x2d <= b && b <= 0x2f;
            if (!(case1 || case2)) {
                return false;
            }
            bkgData = {};
            if (a === 0x10 || a === 0x18) {
                index = Math.floor((b - 0x20) / 2);
                bkgData.background = backgroundColors[index];
                if (b % 2 === 1) {
                    bkgData.background = bkgData.background + '_semi';
                }
            } else if (b === 0x2d) {
                bkgData.background = 'transparent';
            } else {
                bkgData.foreground = 'black';
                if (b === 0x2f) {
                    bkgData.underline = true;
                }
            }
            chNr = a < 0x18 ? 1 : 2;
            channel = this.channels[chNr - 1];
            channel.setBkgData(bkgData);
            this.lastCmdA = null;
            this.lastCmdB = null;
            return true;
        }

        /**
         * Reset state of parser and its channels.
         */

    }, {
        key: 'reset',
        value: function reset() {
            for (var i = 0; i < this.channels.length; i++) {
                if (this.channels[i]) {
                    this.channels[i].reset();
                }
            }
            this.lastCmdA = null;
            this.lastCmdB = null;
        }

        /**
         * Trigger the generation of a cue, and the start of a new one if displayScreens are not empty.
         */

    }, {
        key: 'cueSplitAtTime',
        value: function cueSplitAtTime(t) {
            for (var i = 0; i < this.channels.length; i++) {
                if (this.channels[i]) {
                    this.channels[i].cueSplitAtTime(t);
                }
            }
        }
    }]);

    return Cea608Parser;
}();

exports.default = Cea608Parser;

},{}],50:[function(_dereq_,module,exports){
'use strict';

var _vttparser = _dereq_(57);

var Cues = {

  newCue: function newCue(track, startTime, endTime, captionScreen) {
    var row;
    var cue;
    var indenting;
    var indent;
    var text;
    var VTTCue = window.VTTCue || window.TextTrackCue;

    for (var r = 0; r < captionScreen.rows.length; r++) {
      row = captionScreen.rows[r];
      indenting = true;
      indent = 0;
      text = '';

      if (!row.isEmpty()) {
        for (var c = 0; c < row.chars.length; c++) {
          if (row.chars[c].uchar.match(/\s/) && indenting) {
            indent++;
          } else {
            text += row.chars[c].uchar;
            indenting = false;
          }
        }
        //To be used for cleaning-up orphaned roll-up captions
        row.cueStartTime = startTime;

        // Give a slight bump to the endTime if it's equal to startTime to avoid a SyntaxError in IE
        if (startTime === endTime) {
          endTime += 0.0001;
        }

        cue = new VTTCue(startTime, endTime, (0, _vttparser.fixLineBreaks)(text.trim()));

        if (indent >= 16) {
          indent--;
        } else {
          indent++;
        }

        // VTTCue.line get's flakey when using controls, so let's now include line 13&14
        // also, drop line 1 since it's to close to the top
        if (navigator.userAgent.match(/Firefox\//)) {
          cue.line = r + 1;
        } else {
          cue.line = r > 7 ? r - 2 : r + 1;
        }
        cue.align = 'left';
        // Clamp the position between 0 and 100 - if out of these bounds, Firefox throws an exception and captions break
        cue.position = Math.max(0, Math.min(100, 100 * (indent / 32) + (navigator.userAgent.match(/Firefox\//) ? 50 : 0)));
        track.addCue(cue);
      }
    }
  }

};

module.exports = Cues;

},{"57":57}],51:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
   value: true
});
exports.findFragWithCC = findFragWithCC;

var _binarySearch = _dereq_(48);

var _binarySearch2 = _interopRequireDefault(_binarySearch);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function findFragWithCC(fragments, CC) {
   return _binarySearch2.default.search(fragments, function (candidate) {
      if (candidate.cc < CC) {
         return 1;
      } else if (candidate.cc > CC) {
         return -1;
      } else {
         return 0;
      }
   });
}

},{"48":48}],52:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }(); /*
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * EWMA Bandwidth Estimator
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      *  - heavily inspired from shaka-player
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * Tracks bandwidth samples and estimates available bandwidth.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * Based on the minimum of two exponentially-weighted moving averages with
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * different half-lives.
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      */

var _ewma = _dereq_(53);

var _ewma2 = _interopRequireDefault(_ewma);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var EwmaBandWidthEstimator = function () {
  function EwmaBandWidthEstimator(hls, slow, fast, defaultEstimate) {
    _classCallCheck(this, EwmaBandWidthEstimator);

    this.hls = hls;
    this.defaultEstimate_ = defaultEstimate;
    this.minWeight_ = 0.001;
    this.minDelayMs_ = 50;
    this.slow_ = new _ewma2.default(slow);
    this.fast_ = new _ewma2.default(fast);
  }

  _createClass(EwmaBandWidthEstimator, [{
    key: 'sample',
    value: function sample(durationMs, numBytes) {
      durationMs = Math.max(durationMs, this.minDelayMs_);
      var bandwidth = 8000 * numBytes / durationMs,

      //console.log('instant bw:'+ Math.round(bandwidth));
      // we weight sample using loading duration....
      weight = durationMs / 1000;
      this.fast_.sample(weight, bandwidth);
      this.slow_.sample(weight, bandwidth);
    }
  }, {
    key: 'canEstimate',
    value: function canEstimate() {
      var fast = this.fast_;
      return fast && fast.getTotalWeight() >= this.minWeight_;
    }
  }, {
    key: 'getEstimate',
    value: function getEstimate() {
      if (this.canEstimate()) {
        //console.log('slow estimate:'+ Math.round(this.slow_.getEstimate()));
        //console.log('fast estimate:'+ Math.round(this.fast_.getEstimate()));
        // Take the minimum of these two estimates.  This should have the effect of
        // adapting down quickly, but up more slowly.
        return Math.min(this.fast_.getEstimate(), this.slow_.getEstimate());
      } else {
        return this.defaultEstimate_;
      }
    }
  }, {
    key: 'destroy',
    value: function destroy() {}
  }]);

  return EwmaBandWidthEstimator;
}();

exports.default = EwmaBandWidthEstimator;

},{"53":53}],53:[function(_dereq_,module,exports){
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

/*
 * compute an Exponential Weighted moving average
 * - https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
 *  - heavily inspired from shaka-player
 */

var EWMA = function () {

  //  About half of the estimated value will be from the last |halfLife| samples by weight.
  function EWMA(halfLife) {
    _classCallCheck(this, EWMA);

    // Larger values of alpha expire historical data more slowly.
    this.alpha_ = halfLife ? Math.exp(Math.log(0.5) / halfLife) : 0;
    this.estimate_ = 0;
    this.totalWeight_ = 0;
  }

  _createClass(EWMA, [{
    key: "sample",
    value: function sample(weight, value) {
      var adjAlpha = Math.pow(this.alpha_, weight);
      this.estimate_ = value * (1 - adjAlpha) + adjAlpha * this.estimate_;
      this.totalWeight_ += weight;
    }
  }, {
    key: "getTotalWeight",
    value: function getTotalWeight() {
      return this.totalWeight_;
    }
  }, {
    key: "getEstimate",
    value: function getEstimate() {
      if (this.alpha_) {
        var zeroFactor = 1 - Math.pow(this.alpha_, this.totalWeight_);
        return this.estimate_ / zeroFactor;
      } else {
        return this.estimate_;
      }
    }
  }]);

  return EWMA;
}();

exports.default = EWMA;

},{}],54:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; };

function noop() {}

var fakeLogger = {
  trace: noop,
  debug: noop,
  log: noop,
  warn: noop,
  info: noop,
  error: noop
};

var exportedLogger = fakeLogger;

/*globals self: false */

//let lastCallTime;
// function formatMsgWithTimeInfo(type, msg) {
//   const now = Date.now();
//   const diff = lastCallTime ? '+' + (now - lastCallTime) : '0';
//   lastCallTime = now;
//   msg = (new Date(now)).toISOString() + ' | [' +  type + '] > ' + msg + ' ( ' + diff + ' ms )';
//   return msg;
// }

function formatMsg(type, msg) {
  msg = '[' + type + '] > ' + msg;
  return msg;
}

function consolePrintFn(type) {
  var func = self.console[type];
  if (func) {
    return function () {
      for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
        args[_key] = arguments[_key];
      }

      if (args[0]) {
        args[0] = formatMsg(type, args[0]);
      }
      func.apply(self.console, args);
    };
  }
  return noop;
}

function exportLoggerFunctions(debugConfig) {
  for (var _len2 = arguments.length, functions = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
    functions[_key2 - 1] = arguments[_key2];
  }

  functions.forEach(function (type) {
    exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type);
  });
}

var enableLogs = exports.enableLogs = function enableLogs(debugConfig) {
  if (debugConfig === true || (typeof debugConfig === 'undefined' ? 'undefined' : _typeof(debugConfig)) === 'object') {
    exportLoggerFunctions(debugConfig,
    // Remove out from list here to hard-disable a log-level
    //'trace',
    'debug', 'log', 'info', 'warn', 'error');
    // Some browsers don't allow to use bind on console object anyway
    // fallback to default if needed
    try {
      exportedLogger.log();
    } catch (e) {
      exportedLogger = fakeLogger;
    }
  } else {
    exportedLogger = fakeLogger;
  }
};

var logger = exports.logger = exportedLogger;

},{}],55:[function(_dereq_,module,exports){
'use strict';

/**
 *  TimeRanges to string helper
 */

var TimeRanges = {
  toString: function toString(r) {
    var log = '',
        len = r.length;
    for (var i = 0; i < len; i++) {
      log += '[' + r.start(i).toFixed(3) + ',' + r.end(i).toFixed(3) + ']';
    }
    return log;
  }
};

module.exports = TimeRanges;

},{}],56:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

/**
 * Copyright 2013 vtt.js Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

exports.default = function () {
  if (typeof window !== 'undefined' && window.VTTCue) {
    return window.VTTCue;
  }

  var autoKeyword = 'auto';
  var directionSetting = {
    '': true,
    lr: true,
    rl: true
  };
  var alignSetting = {
    start: true,
    middle: true,
    end: true,
    left: true,
    right: true
  };

  function findDirectionSetting(value) {
    if (typeof value !== 'string') {
      return false;
    }
    var dir = directionSetting[value.toLowerCase()];
    return dir ? value.toLowerCase() : false;
  }

  function findAlignSetting(value) {
    if (typeof value !== 'string') {
      return false;
    }
    var align = alignSetting[value.toLowerCase()];
    return align ? value.toLowerCase() : false;
  }

  function extend(obj) {
    var i = 1;
    for (; i < arguments.length; i++) {
      var cobj = arguments[i];
      for (var p in cobj) {
        obj[p] = cobj[p];
      }
    }

    return obj;
  }

  function VTTCue(startTime, endTime, text) {
    var cue = this;
    var isIE8 = function () {
      if (typeof navigator === 'undefined') {
        return;
      }
      return (/MSIE\s8\.0/.test(navigator.userAgent)
      );
    }();
    var baseObj = {};

    if (isIE8) {
      cue = document.createElement('custom');
    } else {
      baseObj.enumerable = true;
    }

    /**
     * Shim implementation specific properties. These properties are not in
     * the spec.
     */

    // Lets us know when the VTTCue's data has changed in such a way that we need
    // to recompute its display state. This lets us compute its display state
    // lazily.
    cue.hasBeenReset = false;

    /**
     * VTTCue and TextTrackCue properties
     * http://dev.w3.org/html5/webvtt/#vttcue-interface
     */

    var _id = '';
    var _pauseOnExit = false;
    var _startTime = startTime;
    var _endTime = endTime;
    var _text = text;
    var _region = null;
    var _vertical = '';
    var _snapToLines = true;
    var _line = 'auto';
    var _lineAlign = 'start';
    var _position = 50;
    var _positionAlign = 'middle';
    var _size = 50;
    var _align = 'middle';

    Object.defineProperty(cue, 'id', extend({}, baseObj, {
      get: function get() {
        return _id;
      },
      set: function set(value) {
        _id = '' + value;
      }
    }));

    Object.defineProperty(cue, 'pauseOnExit', extend({}, baseObj, {
      get: function get() {
        return _pauseOnExit;
      },
      set: function set(value) {
        _pauseOnExit = !!value;
      }
    }));

    Object.defineProperty(cue, 'startTime', extend({}, baseObj, {
      get: function get() {
        return _startTime;
      },
      set: function set(value) {
        if (typeof value !== 'number') {
          throw new TypeError('Start time must be set to a number.');
        }
        _startTime = value;
        this.hasBeenReset = true;
      }
    }));

    Object.defineProperty(cue, 'endTime', extend({}, baseObj, {
      get: function get() {
        return _endTime;
      },
      set: function set(value) {
        if (typeof value !== 'number') {
          throw new TypeError('End time must be set to a number.');
        }
        _endTime = value;
        this.hasBeenReset = true;
      }
    }));

    Object.defineProperty(cue, 'text', extend({}, baseObj, {
      get: function get() {
        return _text;
      },
      set: function set(value) {
        _text = '' + value;
        this.hasBeenReset = true;
      }
    }));

    Object.defineProperty(cue, 'region', extend({}, baseObj, {
      get: function get() {
        return _region;
      },
      set: function set(value) {
        _region = value;
        this.hasBeenReset = true;
      }
    }));

    Object.defineProperty(cue, 'vertical', extend({}, baseObj, {
      get: function get() {
        return _vertical;
      },
      set: function set(value) {
        var setting = findDirectionSetting(value);
        // Have to check for false because the setting an be an empty string.
        if (setting === false) {
          throw new SyntaxError('An invalid or illegal string was specified.');
        }
        _vertical = setting;
        this.hasBeenReset = true;
      }
    }));

    Object.defineProperty(cue, 'snapToLines', extend({}, baseObj, {
      get: function get() {
        return _snapToLines;
      },
      set: function set(value) {
        _snapToLines = !!value;
        this.hasBeenReset = true;
      }
    }));

    Object.defineProperty(cue, 'line', extend({}, baseObj, {
      get: function get() {
        return _line;
      },
      set: function set(value) {
        if (typeof value !== 'number' && value !== autoKeyword) {
          throw new SyntaxError('An invalid number or illegal string was specified.');
        }
        _line = value;
        this.hasBeenReset = true;
      }
    }));

    Object.defineProperty(cue, 'lineAlign', extend({}, baseObj, {
      get: function get() {
        return _lineAlign;
      },
      set: function set(value) {
        var setting = findAlignSetting(value);
        if (!setting) {
          throw new SyntaxError('An invalid or illegal string was specified.');
        }
        _lineAlign = setting;
        this.hasBeenReset = true;
      }
    }));

    Object.defineProperty(cue, 'position', extend({}, baseObj, {
      get: function get() {
        return _position;
      },
      set: function set(value) {
        if (value < 0 || value > 100) {
          throw new Error('Position must be between 0 and 100.');
        }
        _position = value;
        this.hasBeenReset = true;
      }
    }));

    Object.defineProperty(cue, 'positionAlign', extend({}, baseObj, {
      get: function get() {
        return _positionAlign;
      },
      set: function set(value) {
        var setting = findAlignSetting(value);
        if (!setting) {
          throw new SyntaxError('An invalid or illegal string was specified.');
        }
        _positionAlign = setting;
        this.hasBeenReset = true;
      }
    }));

    Object.defineProperty(cue, 'size', extend({}, baseObj, {
      get: function get() {
        return _size;
      },
      set: function set(value) {
        if (value < 0 || value > 100) {
          throw new Error('Size must be between 0 and 100.');
        }
        _size = value;
        this.hasBeenReset = true;
      }
    }));

    Object.defineProperty(cue, 'align', extend({}, baseObj, {
      get: function get() {
        return _align;
      },
      set: function set(value) {
        var setting = findAlignSetting(value);
        if (!setting) {
          throw new SyntaxError('An invalid or illegal string was specified.');
        }
        _align = setting;
        this.hasBeenReset = true;
      }
    }));

    /**
     * Other <track> spec defined properties
     */

    // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
    cue.displayState = undefined;

    if (isIE8) {
      return cue;
    }
  }

  /**
   * VTTCue methods
   */

  VTTCue.prototype.getCueAsHTML = function () {
    // Assume WebVTT.convertCueToDOMTree is on the global.
    var WebVTT = window.WebVTT;
    return WebVTT.convertCueToDOMTree(window, this.text);
  };

  return VTTCue;
}();

},{}],57:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.fixLineBreaks = undefined;

var _vttcue = _dereq_(56);

var _vttcue2 = _interopRequireDefault(_vttcue);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

var StringDecoder = function StringDecoder() {
  return {
    decode: function decode(data) {
      if (!data) {
        return '';
      }
      if (typeof data !== 'string') {
        throw new Error('Error - expected string data.');
      }
      return decodeURIComponent(encodeURIComponent(data));
    }
  };
}; /*
    * Source: https://github.com/mozilla/vtt.js/blob/master/dist/vtt.js#L1716
    */

function VTTParser() {
  this.window = window;
  this.state = 'INITIAL';
  this.buffer = '';
  this.decoder = new StringDecoder();
  this.regionList = [];
}

// Try to parse input as a time stamp.
function parseTimeStamp(input) {

  function computeSeconds(h, m, s, f) {
    return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
  }

  var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);
  if (!m) {
    return null;
  }

  if (m[3]) {
    // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
    return computeSeconds(m[1], m[2], m[3].replace(':', ''), m[4]);
  } else if (m[1] > 59) {
    // Timestamp takes the form of [hours]:[minutes].[milliseconds]
    // First position is hours as it's over 59.
    return computeSeconds(m[1], m[2], 0, m[4]);
  } else {
    // Timestamp takes the form of [minutes]:[seconds].[milliseconds]
    return computeSeconds(0, m[1], m[2], m[4]);
  }
}

// A settings object holds key/value pairs and will ignore anything but the first
// assignment to a specific key.
function Settings() {
  this.values = Object.create(null);
}

Settings.prototype = {
  // Only accept the first assignment to any key.
  set: function set(k, v) {
    if (!this.get(k) && v !== '') {
      this.values[k] = v;
    }
  },
  // Return the value for a key, or a default value.
  // If 'defaultKey' is passed then 'dflt' is assumed to be an object with
  // a number of possible default values as properties where 'defaultKey' is
  // the key of the property that will be chosen; otherwise it's assumed to be
  // a single value.
  get: function get(k, dflt, defaultKey) {
    if (defaultKey) {
      return this.has(k) ? this.values[k] : dflt[defaultKey];
    }
    return this.has(k) ? this.values[k] : dflt;
  },
  // Check whether we have a value for a key.
  has: function has(k) {
    return k in this.values;
  },
  // Accept a setting if its one of the given alternatives.
  alt: function alt(k, v, a) {
    for (var n = 0; n < a.length; ++n) {
      if (v === a[n]) {
        this.set(k, v);
        break;
      }
    }
  },
  // Accept a setting if its a valid (signed) integer.
  integer: function integer(k, v) {
    if (/^-?\d+$/.test(v)) {
      // integer
      this.set(k, parseInt(v, 10));
    }
  },
  // Accept a setting if its a valid percentage.
  percent: function percent(k, v) {
    var m;
    if (m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/)) {
      v = parseFloat(v);
      if (v >= 0 && v <= 100) {
        this.set(k, v);
        return true;
      }
    }
    return false;
  }
};

// Helper function to parse input into groups separated by 'groupDelim', and
// interprete each group as a key/value pair separated by 'keyValueDelim'.
function parseOptions(input, callback, keyValueDelim, groupDelim) {
  var groups = groupDelim ? input.split(groupDelim) : [input];
  for (var i in groups) {
    if (typeof groups[i] !== 'string') {
      continue;
    }
    var kv = groups[i].split(keyValueDelim);
    if (kv.length !== 2) {
      continue;
    }
    var k = kv[0];
    var v = kv[1];
    callback(k, v);
  }
}

var defaults = new _vttcue2.default(0, 0, 0);
// 'middle' was changed to 'center' in the spec: https://github.com/w3c/webvtt/pull/244
// Chrome and Safari don't yet support this change, but FF does
var center = defaults.align === 'middle' ? 'middle' : 'center';

function parseCue(input, cue, regionList) {
  // Remember the original input if we need to throw an error.
  var oInput = input;
  // 4.1 WebVTT timestamp
  function consumeTimeStamp() {
    var ts = parseTimeStamp(input);
    if (ts === null) {
      throw new Error('Malformed timestamp: ' + oInput);
    }
    // Remove time stamp from input.
    input = input.replace(/^[^\sa-zA-Z-]+/, '');
    return ts;
  }

  // 4.4.2 WebVTT cue settings
  function consumeCueSettings(input, cue) {
    var settings = new Settings();

    parseOptions(input, function (k, v) {
      switch (k) {
        case 'region':
          // Find the last region we parsed with the same region id.
          for (var i = regionList.length - 1; i >= 0; i--) {
            if (regionList[i].id === v) {
              settings.set(k, regionList[i].region);
              break;
            }
          }
          break;
        case 'vertical':
          settings.alt(k, v, ['rl', 'lr']);
          break;
        case 'line':
          var vals = v.split(','),
              vals0 = vals[0];
          settings.integer(k, vals0);
          if (settings.percent(k, vals0)) {
            settings.set('snapToLines', false);
          }
          settings.alt(k, vals0, ['auto']);
          if (vals.length === 2) {
            settings.alt('lineAlign', vals[1], ['start', center, 'end']);
          }
          break;
        case 'position':
          vals = v.split(',');
          settings.percent(k, vals[0]);
          if (vals.length === 2) {
            settings.alt('positionAlign', vals[1], ['start', center, 'end', 'line-left', 'line-right', 'auto']);
          }
          break;
        case 'size':
          settings.percent(k, v);
          break;
        case 'align':
          settings.alt(k, v, ['start', center, 'end', 'left', 'right']);
          break;
      }
    }, /:/, /\s/);

    // Apply default values for any missing fields.
    cue.region = settings.get('region', null);
    cue.vertical = settings.get('vertical', '');
    var line = settings.get('line', 'auto');
    if (line === 'auto' && defaults.line === -1) {
      // set numeric line number for Safari
      line = -1;
    }
    cue.line = line;
    cue.lineAlign = settings.get('lineAlign', 'start');
    cue.snapToLines = settings.get('snapToLines', true);
    cue.size = settings.get('size', 100);
    cue.align = settings.get('align', center);
    var position = settings.get('position', 'auto');
    if (position === 'auto' && defaults.position === 50) {
      // set numeric position for Safari
      position = cue.align === 'start' || cue.align === 'left' ? 0 : cue.align === 'end' || cue.align === 'right' ? 100 : 50;
    }
    cue.position = position;
  }

  function skipWhitespace() {
    input = input.replace(/^\s+/, '');
  }

  // 4.1 WebVTT cue timings.
  skipWhitespace();
  cue.startTime = consumeTimeStamp(); // (1) collect cue start time
  skipWhitespace();
  if (input.substr(0, 3) !== '-->') {
    // (3) next characters must match '-->'
    throw new Error('Malformed time stamp (time stamps must be separated by \'-->\'): ' + oInput);
  }
  input = input.substr(3);
  skipWhitespace();
  cue.endTime = consumeTimeStamp(); // (5) collect cue end time

  // 4.1 WebVTT cue settings list.
  skipWhitespace();
  consumeCueSettings(input, cue);
}

function fixLineBreaks(input) {
  return input.replace(/<br(?: \/)?>/gi, '\n');
}

VTTParser.prototype = {
  parse: function parse(data) {
    var self = this;

    // If there is no data then we won't decode it, but will just try to parse
    // whatever is in buffer already. This may occur in circumstances, for
    // example when flush() is called.
    if (data) {
      // Try to decode the data that we received.
      self.buffer += self.decoder.decode(data, { stream: true });
    }

    function collectNextLine() {
      var buffer = self.buffer;
      var pos = 0;

      buffer = fixLineBreaks(buffer);

      while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
        ++pos;
      }
      var line = buffer.substr(0, pos);
      // Advance the buffer early in case we fail below.
      if (buffer[pos] === '\r') {
        ++pos;
      }
      if (buffer[pos] === '\n') {
        ++pos;
      }
      self.buffer = buffer.substr(pos);
      return line;
    }

    // 3.2 WebVTT metadata header syntax
    function parseHeader(input) {
      parseOptions(input, function (k, v) {
        switch (k) {
          case 'Region':
            // 3.3 WebVTT region metadata header syntax
            console.log('parse region', v);
            //parseRegion(v);
            break;
        }
      }, /:/);
    }

    // 5.1 WebVTT file parsing.
    try {
      var line;
      if (self.state === 'INITIAL') {
        // We can't start parsing until we have the first line.
        if (!/\r\n|\n/.test(self.buffer)) {
          return this;
        }

        line = collectNextLine();

        var m = line.match(/^WEBVTT([ \t].*)?$/);
        if (!m || !m[0]) {
          throw new Error('Malformed WebVTT signature.');
        }

        self.state = 'HEADER';
      }

      var alreadyCollectedLine = false;
      while (self.buffer) {
        // We can't parse a line until we have the full line.
        if (!/\r\n|\n/.test(self.buffer)) {
          return this;
        }

        if (!alreadyCollectedLine) {
          line = collectNextLine();
        } else {
          alreadyCollectedLine = false;
        }

        switch (self.state) {
          case 'HEADER':
            // 13-18 - Allow a header (metadata) under the WEBVTT line.
            if (/:/.test(line)) {
              parseHeader(line);
            } else if (!line) {
              // An empty line terminates the header and starts the body (cues).
              self.state = 'ID';
            }
            continue;
          case 'NOTE':
            // Ignore NOTE blocks.
            if (!line) {
              self.state = 'ID';
            }
            continue;
          case 'ID':
            // Check for the start of NOTE blocks.
            if (/^NOTE($|[ \t])/.test(line)) {
              self.state = 'NOTE';
              break;
            }
            // 19-29 - Allow any number of line terminators, then initialize new cue values.
            if (!line) {
              continue;
            }
            self.cue = new _vttcue2.default(0, 0, '');
            self.state = 'CUE';
            // 30-39 - Check if self line contains an optional identifier or timing data.
            if (line.indexOf('-->') === -1) {
              self.cue.id = line;
              continue;
            }
          // Process line as start of a cue.
          /*falls through*/
          case 'CUE':
            // 40 - Collect cue timings and settings.
            try {
              parseCue(line, self.cue, self.regionList);
            } catch (e) {
              // In case of an error ignore rest of the cue.
              self.cue = null;
              self.state = 'BADCUE';
              continue;
            }
            self.state = 'CUETEXT';
            continue;
          case 'CUETEXT':
            var hasSubstring = line.indexOf('-->') !== -1;
            // 34 - If we have an empty line then report the cue.
            // 35 - If we have the special substring '-->' then report the cue,
            // but do not collect the line as we need to process the current
            // one as a new cue.
            if (!line || hasSubstring && (alreadyCollectedLine = true)) {
              // We are done parsing self cue.
              if (self.oncue) {
                self.oncue(self.cue);
              }
              self.cue = null;
              self.state = 'ID';
              continue;
            }
            if (self.cue.text) {
              self.cue.text += '\n';
            }
            self.cue.text += line;
            continue;
          case 'BADCUE':
            // BADCUE
            // 54-62 - Collect and discard the remaining cue.
            if (!line) {
              self.state = 'ID';
            }
            continue;
        }
      }
    } catch (e) {

      // If we are currently parsing a cue, report what we have.
      if (self.state === 'CUETEXT' && self.cue && self.oncue) {
        self.oncue(self.cue);
      }
      self.cue = null;
      // Enter BADWEBVTT state if header was not parsed correctly otherwise
      // another exception occurred so enter BADCUE state.
      self.state = self.state === 'INITIAL' ? 'BADWEBVTT' : 'BADCUE';
    }
    return this;
  },
  flush: function flush() {
    var self = this;
    try {
      // Finish decoding the stream.
      self.buffer += self.decoder.decode();
      // Synthesize the end of the current cue or region.
      if (self.cue || self.state === 'HEADER') {
        self.buffer += '\n\n';
        self.parse();
      }
      // If we've flushed, parsed, and we're still on the INITIAL state then
      // that means we don't have enough of the stream to parse the first
      // line.
      if (self.state === 'INITIAL') {
        throw new Error('Malformed WebVTT signature.');
      }
    } catch (e) {
      throw e;
    }
    if (self.onflush) {
      self.onflush();
    }
    return this;
  }
};

exports.fixLineBreaks = fixLineBreaks;
exports.default = VTTParser;

},{"56":56}],58:[function(_dereq_,module,exports){
'use strict';

var _vttparser = _dereq_(57);

var _vttparser2 = _interopRequireDefault(_vttparser);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

// String.prototype.startsWith is not supported in IE11
var startsWith = function startsWith(inputString, searchString, position) {
    return inputString.substr(position || 0, searchString.length) === searchString;
};

var cueString2millis = function cueString2millis(timeString) {
    var ts = parseInt(timeString.substr(-3));
    var secs = parseInt(timeString.substr(-6, 2));
    var mins = parseInt(timeString.substr(-9, 2));
    var hours = timeString.length > 9 ? parseInt(timeString.substr(0, timeString.indexOf(':'))) : 0;

    if (isNaN(ts) || isNaN(secs) || isNaN(mins) || isNaN(hours)) {
        return -1;
    }

    ts += 1000 * secs;
    ts += 60 * 1000 * mins;
    ts += 60 * 60 * 1000 * hours;

    return ts;
};

// From https://github.com/darkskyapp/string-hash
var hash = function hash(text) {
    var hash = 5381;
    var i = text.length;
    while (i) {
        hash = hash * 33 ^ text.charCodeAt(--i);
    }
    return (hash >>> 0).toString();
};

var calculateOffset = function calculateOffset(vttCCs, cc, presentationTime) {
    var currCC = vttCCs[cc];
    var prevCC = vttCCs[currCC.prevCC];

    // This is the first discontinuity or cues have been processed since the last discontinuity
    // Offset = current discontinuity time
    if (!prevCC || !prevCC.new && currCC.new) {
        vttCCs.ccOffset = vttCCs.presentationOffset = currCC.start;
        currCC.new = false;
        return;
    }

    // There have been discontinuities since cues were last parsed.
    // Offset = time elapsed
    while (prevCC && prevCC.new) {
        vttCCs.ccOffset += currCC.start - prevCC.start;
        currCC.new = false;
        currCC = prevCC;
        prevCC = vttCCs[currCC.prevCC];
    }

    vttCCs.presentationOffset = presentationTime;
};

var WebVTTParser = {
    parse: function parse(vttByteArray, syncPTS, vttCCs, cc, callBack, errorCallBack) {
        // Convert byteArray into string, replacing any somewhat exotic linefeeds with "\n", then split on that character.
        var re = /\r\n|\n\r|\n|\r/g;
        var vttLines = String.fromCharCode.apply(null, new Uint8Array(vttByteArray)).trim().replace(re, '\n').split('\n');
        var cueTime = '00:00.000';
        var mpegTs = 0;
        var localTime = 0;
        var presentationTime = 0;
        var cues = [];
        var parsingError = void 0;
        var inHeader = true;
        // let VTTCue = VTTCue || window.TextTrackCue;

        // Create parser object using VTTCue with TextTrackCue fallback on certain browsers.
        var parser = new _vttparser2.default();

        parser.oncue = function (cue) {
            // Adjust cue timing; clamp cues to start no earlier than - and drop cues that don't end after - 0 on timeline.
            var currCC = vttCCs[cc];
            var cueOffset = vttCCs.ccOffset;

            // Update offsets for new discontinuities
            if (currCC && currCC.new) {
                if (localTime !== undefined) {
                    // When local time is provided, offset = discontinuity start time - local time
                    cueOffset = vttCCs.ccOffset = currCC.start;
                } else {
                    calculateOffset(vttCCs, cc, presentationTime);
                }
            }

            if (presentationTime) {
                // If we have MPEGTS, offset = presentation time + discontinuity offset
                cueOffset = presentationTime + vttCCs.ccOffset - vttCCs.presentationOffset;
            }

            cue.startTime += cueOffset - localTime;
            cue.endTime += cueOffset - localTime;

            // Create a unique hash id for a cue based on start/end times and text.
            // This helps timeline-controller to avoid showing repeated captions.
            cue.id = hash(cue.startTime) + hash(cue.endTime) + hash(cue.text);

            // Fix encoding of special characters. TODO: Test with all sorts of weird characters.
            cue.text = decodeURIComponent(escape(cue.text));
            if (cue.endTime > 0) {
                cues.push(cue);
            }
        };

        parser.onparsingerror = function (e) {
            parsingError = e;
        };

        parser.onflush = function () {
            if (parsingError && errorCallBack) {
                errorCallBack(parsingError);
                return;
            }
            callBack(cues);
        };

        // Go through contents line by line.
        vttLines.forEach(function (line) {
            if (inHeader) {
                // Look for X-TIMESTAMP-MAP in header.
                if (startsWith(line, 'X-TIMESTAMP-MAP=')) {
                    // Once found, no more are allowed anyway, so stop searching.
                    inHeader = false;
                    // Extract LOCAL and MPEGTS.
                    line.substr(16).split(',').forEach(function (timestamp) {
                        if (startsWith(timestamp, 'LOCAL:')) {
                            cueTime = timestamp.substr(6);
                        } else if (startsWith(timestamp, 'MPEGTS:')) {
                            mpegTs = parseInt(timestamp.substr(7));
                        }
                    });
                    try {
                        // Calculate subtitle offset in milliseconds.
                        // If sync PTS is less than zero, we have a 33-bit wraparound, which is fixed by adding 2^33 = 8589934592.
                        syncPTS = syncPTS < 0 ? syncPTS + 8589934592 : syncPTS;
                        // Adjust MPEGTS by sync PTS.
                        mpegTs -= syncPTS;
                        // Convert cue time to seconds
                        localTime = cueString2millis(cueTime) / 1000;
                        // Convert MPEGTS to seconds from 90kHz.
                        presentationTime = mpegTs / 90000;

                        if (localTime === -1) {
                            parsingError = new Error('Malformed X-TIMESTAMP-MAP: ' + line);
                        }
                    } catch (e) {
                        parsingError = new Error('Malformed X-TIMESTAMP-MAP: ' + line);
                    }
                    // Return without parsing X-TIMESTAMP-MAP line.
                    return;
                } else if (line === '') {
                    inHeader = false;
                }
            }
            // Parse line by default.
            parser.parse(line + '\n');
        });

        parser.flush();
    }
};

module.exports = WebVTTParser;

},{"57":57}],59:[function(_dereq_,module,exports){
'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _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; }; }(); /**
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      * XHR based logger
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     */

var _logger = _dereq_(54);

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var XhrLoader = function () {
  function XhrLoader(config) {
    _classCallCheck(this, XhrLoader);

    if (config && config.xhrSetup) {
      this.xhrSetup = config.xhrSetup;
    }
  }

  _createClass(XhrLoader, [{
    key: 'destroy',
    value: function destroy() {
      this.abort();
      this.loader = null;
    }
  }, {
    key: 'abort',
    value: function abort() {
      var loader = this.loader;
      if (loader && loader.readyState !== 4) {
        this.stats.aborted = true;
        loader.abort();
      }

      window.clearTimeout(this.requestTimeout);
      this.requestTimeout = null;
      window.clearTimeout(this.retryTimeout);
      this.retryTimeout = null;
    }
  }, {
    key: 'load',
    value: function load(context, config, callbacks) {
      this.context = context;
      this.config = config;
      this.callbacks = callbacks;
      this.stats = { trequest: performance.now(), retry: 0 };
      this.retryDelay = config.retryDelay;
      this.loadInternal();
    }
  }, {
    key: 'loadInternal',
    value: function loadInternal() {
      var xhr,
          context = this.context;

      if (typeof XDomainRequest !== 'undefined') {
        xhr = this.loader = new XDomainRequest();
      } else {
        xhr = this.loader = new XMLHttpRequest();
      }
      var stats = this.stats;
      stats.tfirst = 0;
      stats.loaded = 0;
      var xhrSetup = this.xhrSetup;

      try {
        if (xhrSetup) {
          try {
            xhrSetup(xhr, context.url);
          } catch (e) {
            // fix xhrSetup: (xhr, url) => {xhr.setRequestHeader("Content-Language", "test");}
            // not working, as xhr.setRequestHeader expects xhr.readyState === OPEN
            xhr.open('GET', context.url, true);
            xhrSetup(xhr, context.url);
          }
        }
        if (!xhr.readyState) {
          xhr.open('GET', context.url, true);
        }
      } catch (e) {
        // IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS
        this.callbacks.onError({ code: xhr.status, text: e.message }, context, xhr);
        return;
      }

      if (context.rangeEnd) {
        xhr.setRequestHeader('Range', 'bytes=' + context.rangeStart + '-' + (context.rangeEnd - 1));
      }
      xhr.onreadystatechange = this.readystatechange.bind(this);
      xhr.onprogress = this.loadprogress.bind(this);
      xhr.responseType = context.responseType;

      // setup timeout before we perform request
      this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), this.config.timeout);
      xhr.send();
    }
  }, {
    key: 'readystatechange',
    value: function readystatechange(event) {
      var xhr = event.currentTarget,
          readyState = xhr.readyState,
          stats = this.stats,
          context = this.context,
          config = this.config;

      // don't proceed if xhr has been aborted
      if (stats.aborted) {
        return;
      }

      // >= HEADERS_RECEIVED
      if (readyState >= 2) {
        // clear xhr timeout and rearm it if readyState less than 4
        window.clearTimeout(this.requestTimeout);
        if (stats.tfirst === 0) {
          stats.tfirst = Math.max(performance.now(), stats.trequest);
        }
        if (readyState === 4) {
          var status = xhr.status;
          // http status between 200 to 299 are all successful
          if (status >= 200 && status < 300) {
            stats.tload = Math.max(stats.tfirst, performance.now());
            var data = void 0,
                len = void 0;
            if (context.responseType === 'arraybuffer') {
              data = xhr.response;
              len = data.byteLength;
            } else {
              data = xhr.responseText;
              len = data.length;
            }
            stats.loaded = stats.total = len;
            var response = { url: xhr.responseURL, data: data };
            this.callbacks.onSuccess(response, stats, context, xhr);
          } else {
            // if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error
            if (stats.retry >= config.maxRetry || status >= 400 && status < 499) {
              _logger.logger.error(status + ' while loading ' + context.url);
              this.callbacks.onError({ code: status, text: xhr.statusText }, context, xhr);
            } else {
              // retry
              _logger.logger.warn(status + ' while loading ' + context.url + ', retrying in ' + this.retryDelay + '...');
              // aborts and resets internal state
              this.destroy();
              // schedule retry
              this.retryTimeout = window.setTimeout(this.loadInternal.bind(this), this.retryDelay);
              // set exponential backoff
              this.retryDelay = Math.min(2 * this.retryDelay, config.maxRetryDelay);
              stats.retry++;
            }
          }
        } else {
          // readyState >= 2 AND readyState !==4 (readyState = HEADERS_RECEIVED || LOADING) rearm timeout as xhr not finished yet
          this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), config.timeout);
        }
      }
    }
  }, {
    key: 'loadtimeout',
    value: function loadtimeout() {
      _logger.logger.warn('timeout while loading ' + this.context.url);
      this.callbacks.onTimeout(this.stats, this.context, null);
    }
  }, {
    key: 'loadprogress',
    value: function loadprogress(event) {
      var xhr = event.currentTarget,
          stats = this.stats;

      stats.loaded = event.loaded;
      if (event.lengthComputable) {
        stats.total = event.total;
      }
      var onProgress = this.callbacks.onProgress;
      if (onProgress) {
        // third arg is to provide on progress data
        onProgress(stats, this.context, null, xhr);
      }
    }
  }]);

  return XhrLoader;
}();

exports.default = XhrLoader;

},{"54":54}]},{},[40])(40)
});


}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})

},{}]},{},[3])(3)
});

// REQUIRE_END: hola_videojs_hls.js
return res;
};
hola_player_init();
})();
//# sourceMappingURL=hola_player.dash.dev.js.map